Share via


Overzicht van profilering

Een profiler is een hulpprogramma dat de uitvoering van een andere toepassing bewaakt. Een CLR-profiler (Common Language Runtime) is een DLL (Dynamic Link Library) die bestaat uit functies die berichten ontvangen van en berichten verzenden naar de CLR met behulp van de profilerings-API. De profiler-DLL wordt tijdens runtime door de CLR geladen.

Traditionele profileringshulpprogramma's richten zich op het meten van de uitvoering van de toepassing. Dat wil gezegd: ze meten de tijd die wordt besteed aan elke functie of het geheugengebruik van de toepassing in de loop van de tijd. De profilerings-API is gericht op een bredere klasse diagnostische hulpprogramma's, zoals hulpprogramma's voor codedekking en zelfs geavanceerde hulpmiddelen voor foutopsporing. Deze toepassingen zijn allemaal diagnostisch van aard. De profilerings-API meet niet alleen, maar bewaakt ook de uitvoering van een toepassing. Daarom mag de profilerings-API nooit worden gebruikt door de toepassing zelf en mag de uitvoering van de toepassing niet afhankelijk zijn van (of worden beïnvloed door) de profiler.

Het profileren van een CLR-toepassing vereist meer ondersteuning dan het conventioneel compileren van machinecode. Dit komt doordat de CLR concepten introduceert zoals toepassingsdomeinen, garbagecollection, verwerking van beheerde uitzonderingen, Just-In-Time-compilatie van code (het converteren van algemene tussenliggende taal of CIL, code in systeemeigen computercode) en vergelijkbare functies. Conventionele profileringsmechanismen kunnen geen nuttige informatie over deze functies identificeren of bieden. De profilerings-API biedt deze ontbrekende informatie efficiënt, met minimale invloed op de prestaties van de CLR en de geprofileerde toepassing.

JIT-compilatie tijdens runtime biedt goede mogelijkheden voor profilering. Met de profilerings-API kan een profiler de CIL-codestroom in het geheugen voor een routine wijzigen voordat deze wordt gecompileerd met JIT. Op deze manier kan de profiler dynamisch instrumentatiecode toevoegen aan bepaalde routines die dieper moeten worden onderzocht. Hoewel deze aanpak mogelijk is in conventionele scenario's, is het veel eenvoudiger om te implementeren voor de CLR met behulp van de profilerings-API.

De profilerings-API

Normaal gesproken wordt de profilerings-API gebruikt om een code profiler te schrijven. Dit is een programma dat de uitvoering van een beheerde toepassing bewaakt.

De profilerings-API wordt gebruikt door een profiler-DLL, die in hetzelfde proces wordt geladen als de toepassing die wordt geprofileerd. De profiler-DLL implementeert een callback-interface (ICorProfilerCallback in .NET Framework versie 1.0 en 1.1, ICorProfilerCallback2 in versie 2.0 en hoger). De CLR roept de methoden in die interface aan om de profiler op de hoogte te stellen van gebeurtenissen in het geprofileerde proces. De profiler kan terugroepen naar de runtime met behulp van de methoden in de interfaces ICorProfilerInfo en ICorProfilerInfo2 om informatie te verkrijgen over de status van de geprofileerde toepassing.

Notitie

Alleen het gegevensverzamelingsgedeelte van de profileroplossing moet worden uitgevoerd in hetzelfde proces als de geprofileerde toepassing. Alle gebruikersinterfaces en gegevensanalyses moeten in een afzonderlijk proces worden uitgevoerd.

In de volgende afbeelding ziet u hoe de profiler-DLL communiceert met de toepassing die wordt geprofileerd en de CLR.

Schermopname van de profileringsarchitectuur.

De meldingsinterfaces

ICorProfilerCallback en ICorProfilerCallback2 kunnen worden beschouwd als meldingsinterfaces. Deze interfaces bestaan uit methoden zoals ClassLoadStarted, ClassLoadFinished en JITCompilationStarted. Telkens wanneer de CLR een klasse laadt of uitpakt, een functie compileert, enzovoort, wordt de bijbehorende methode aangeroepen in de profiler ICorProfilerCallback of ICorProfilerCallback2 interface.

Een profiler kan bijvoorbeeld de codeprestaties meten via twee meldingsfuncties: FunctionEnter2 en FunctionLeave2. Het is slechts tijdstempels voor elke melding, verzamelt resultaten en voert een lijst uit die aangeeft welke functies de meeste CPU- of wandkloktijd hebben verbruikt tijdens de uitvoering van de toepassing.

De interfaces voor het ophalen van gegevens

De andere belangrijkste interfaces die betrokken zijn bij profilering zijn ICorProfilerInfo en ICorProfilerInfo2. De profiler noemt deze interfaces indien nodig om meer informatie te verkrijgen om de analyse te helpen. Wanneer de CLR bijvoorbeeld de functie FunctionEnter2 aanroept, wordt er een functie-id geleverd. De profiler kan meer informatie over die functie krijgen door de methode ICorProfilerInfo2::GetFunctionInfo2 aan te roepen om de bovenliggende klasse van de functie, de naam, enzovoort te detecteren.

Ondersteunde functies

De profilerings-API biedt informatie over verschillende gebeurtenissen en acties die plaatsvinden in de algemene taalruntime. U kunt deze informatie gebruiken om de interne werking van processen te bewaken en de prestaties van uw .NET Framework-toepassing te analyseren.

De profilerings-API haalt informatie op over de volgende acties en gebeurtenissen die plaatsvinden in de CLR:

  • CLR-opstart- en afsluit-gebeurtenissen.

  • Gebeurtenissen voor het maken en afsluiten van toepassingsdomeinen.

  • Gebeurtenissen voor het laden en lossen van assembly's.

  • Module laden en lossen gebeurtenissen.

  • COM vtable creatie en vernietiging gebeurtenissen.

  • JIT-compilatie- en code-pitchinggebeurtenissen.

  • Klasse laden en lossen gebeurtenissen.

  • Thread-creatie- en vernietigingsgebeurtenissen.

  • Gebeurtenissen voor het invoeren en afsluiten van functies.

  • Uitzonderingen.

  • Overgangen tussen het uitvoeren van beheerde en onbeheerde code.

  • Overgangen tussen verschillende runtimecontexten.

  • Informatie over runtime-schorsingen.

  • Informatie over de heap- en garbagecollection-activiteit van runtimegeheugen.

De profilerings-API kan worden aangeroepen vanuit elke (niet-beheerde) COM-compatibele taal.

De API is efficiënt met betrekking tot CPU- en geheugenverbruik. Profilering omvat geen wijzigingen in de geprofileerde toepassing die significant genoeg zijn om misleidende resultaten te veroorzaken.

De profilerings-API is handig voor zowel sampling als niet-sampling profilers. Een sampling profiler inspecteert het profiel bij normale kloktekens, bijvoorbeeld op 5 milliseconden uit elkaar. Een profiler zonder steekproeven wordt synchroon geïnformeerd over een gebeurtenis met de thread die de gebeurtenis veroorzaakt.

Niet-ondersteunde functionaliteit

De profilerings-API biedt geen ondersteuning voor de volgende functionaliteit:

  • Niet-beheerde code, die moet worden geprofileerd met conventionele Win32-methoden. De CLR profiler bevat echter overgangsgebeurtenissen om de grenzen tussen beheerde en onbeheerde code te bepalen.

  • Zelf wijzigende toepassingen die hun eigen code wijzigen voor doeleinden zoals aspectgeoriënteerd programmeren.

  • Afhankelijkheidscontrole, omdat de profilerings-API deze informatie niet levert. De CLR biedt intrinsieke ondersteuning voor het controleren van alle beheerde code.

  • Externe profilering, die om de volgende redenen niet wordt ondersteund:

    • Externe profilering breidt de uitvoeringstijd uit. Wanneer u de profileringsinterfaces gebruikt, moet u de uitvoeringstijd minimaliseren, zodat profileringsresultaten niet onnodig worden beïnvloed. Dit geldt met name wanneer de uitvoeringsprestaties worden bewaakt. Externe profilering is echter geen beperking wanneer de profileringsinterfaces worden gebruikt om het geheugengebruik te bewaken of om runtime-informatie te verkrijgen over stackframes, objecten enzovoort.

    • De CLR-code profiler moet een of meer callback-interfaces registreren bij de runtime op de lokale computer waarop de geprofileerde toepassing wordt uitgevoerd. Dit beperkt de mogelijkheid om een externe code profiler te maken.

Meldingsthreads

In de meeste gevallen voert de thread waarmee een gebeurtenis wordt gegenereerd ook meldingen uit. Dergelijke meldingen (bijvoorbeeld FunctionEnter en FunctionLeave) hoeven niet expliciet ThreadIDte worden opgegeven. Bovendien kan de profiler besluiten om thread-lokale opslag te gebruiken om de analyseblokken op te slaan en bij te werken in plaats van de analyseblokken in globale opslag te indexeren, op basis van de ThreadID betreffende thread.

Houd er rekening mee dat deze callbacks niet worden geserialiseerd. Gebruikers moeten hun code beveiligen door threadveilige gegevensstructuren te maken en de profilercode waar nodig te vergrendelen om parallelle toegang van meerdere threads te voorkomen. Daarom kunt u in bepaalde gevallen een ongebruikelijke reeks callbacks ontvangen. Stel dat een beheerde toepassing twee threads maakt die identieke code uitvoeren. In dit geval is het mogelijk om een ICorProfilerCallback::JITCompilationStarted-gebeurtenis te ontvangen voor een bepaalde functie van één thread en een FunctionEnter callback van de andere thread voordat de ICorProfilerCallback::JITCompilationFinished callback wordt ontvangen. In dit geval ontvangt de gebruiker een FunctionEnter callback voor een functie die mogelijk nog niet volledig Just-In-Time (JIT) is gecompileerd.

Beveiliging

Een profiler-DLL is een niet-beheerde DLL die wordt uitgevoerd als onderdeel van de uitvoeringsengine van de algemene taalruntime. Als gevolg hiervan is de code in de profiler-DLL niet onderworpen aan de beperkingen van de beveiliging van toegang tot beheerde code. De enige beperkingen voor de profiler-DLL zijn de beperkingen die door het besturingssysteem worden opgelegd aan de gebruiker die de geprofileerde toepassing uitvoert.

Profiler-auteurs moeten passende voorzorgsmaatregelen nemen om beveiligingsproblemen te voorkomen. Tijdens de installatie moet bijvoorbeeld een profiler-DLL worden toegevoegd aan een toegangsbeheerlijst (ACL), zodat een kwaadwillende gebruiker deze niet kan wijzigen.

Beheerde en niet-beheerde code combineren in een Code Profiler

Een onjuist geschreven profiler kan kringverwijzingen naar zichzelf veroorzaken, wat resulteert in onvoorspelbaar gedrag.

Een beoordeling van de CLR-profilerings-API kan de indruk creëren dat u een profiler kunt schrijven die beheerde en onbeheerde onderdelen bevat die elkaar aanroepen via COM-interop- of indirecte aanroepen.

Hoewel dit vanuit ontwerpperspectief mogelijk is, biedt de profilerings-API geen ondersteuning voor beheerde onderdelen. Een CLR profiler moet volledig onbeheerd zijn. Pogingen om beheerde en onbeheerde code in een CLR-profiler te combineren, kunnen toegangsschendingen, programmafouten of impasses veroorzaken. De beheerde onderdelen van de profiler sturen gebeurtenissen terug naar hun onbeheerde onderdelen, die vervolgens de beheerde onderdelen opnieuw zouden aanroepen, wat resulteert in kringverwijzingen.

De enige locatie waar een CLR-profiler beheerde code veilig kan aanroepen, bevindt zich in de hoofdtekst van een methode (Common Intermediate Language). De aanbevolen procedure voor het wijzigen van de CIL-hoofdtekst is het gebruik van de JIT-hercompilatiemethoden in de ICorProfilerCallback4-interface .

Het is ook mogelijk om de oudere instrumentatiemethoden te gebruiken om CIL te wijzigen. Voordat de Just-In-Time-compilatie van een functie is voltooid, kan de profiler beheerde aanroepen invoegen in de hoofdtekst van een methode en deze vervolgens compileren met JIT (zie de methode ICorProfilerInfo::GetILFunctionBody ). Deze techniek kan worden gebruikt voor selectieve instrumentatie van beheerde code of voor het verzamelen van statistieken en prestatiegegevens over de JIT.

Een code profiler kan ook systeemeigen hooks invoegen in de CIL-hoofdtekst van elke beheerde functie die onbeheerde code aanroept. Deze techniek kan worden gebruikt voor instrumentatie en dekking. Een code profiler kan bijvoorbeeld instrumentatiehaken invoegen na elk CIL-blok om ervoor te zorgen dat het blok is uitgevoerd. De wijziging van het CIL-lichaam van een methode is een zeer delicate werking en er zijn veel factoren waarmee rekening moet worden gehouden.

Niet-beheerde code profileren

De CLR-profilerings-API (Common Language Runtime) biedt minimale ondersteuning voor het profileren van onbeheerde code. De volgende functionaliteit is beschikbaar:

  • Opsomming van stapelketens. Met deze functie kan een code profiler de grens tussen beheerde code en niet-beheerde code bepalen.

  • Bepalen of een stack-keten overeenkomt met beheerde code of systeemeigen code.

In .NET Framework-versies 1.0 en 1.1 zijn deze methoden beschikbaar via de in-process subset van de CLR-foutopsporings-API. Ze worden gedefinieerd in het bestand CorDebug.idl.

In .NET Framework 2.0 en hoger kunt u de methode ICorProfilerInfo2::D oStackSnapshot gebruiken voor deze functionaliteit.

COM gebruiken

Hoewel de profileringsinterfaces zijn gedefinieerd als COM-interfaces, initialiseert de Common Language Runtime (CLR) COM niet daadwerkelijk voor het gebruik van deze interfaces. De reden hiervoor is om te voorkomen dat u het threadingmodel moet instellen met behulp van de functie CoInitialize voordat de beheerde toepassing een kans heeft gehad om het gewenste threadingmodel op te geven. Op dezelfde manier moet de profiler zelf geen aanroepen CoInitialize, omdat het een threadingmodel kan kiezen dat niet compatibel is met de toepassing die wordt geprofileerd en de toepassing kan mislukken.

Aanroepstacks

De profilerings-API biedt twee manieren om aanroepstacks te verkrijgen: een momentopnamemethode voor stacks, waarmee het verzamelen van aanroepstacks en een schaduwstackmethode, waarmee de aanroepstack op elk moment wordt bijgehouden.

Momentopname van stack

Een momentopname van een stack is een tracering van de stack van een thread op een moment. De profilerings-API ondersteunt het traceren van beheerde functies op de stack, maar laat het traceren van onbeheerde functies toe aan de eigen stack walker van de profiler.

Zie de methode ICorProfilerInfo2::D oStackSnapshot in deze documentatieset en Profiler Stack Walking in .NET Framework 2.0: Basics and Beyond voor meer informatie over het programmeren van de profiler om beheerde stacks te doorlopen.

Schaduwstack

Als u de momentopnamemethode te vaak gebruikt, kunt u snel een prestatieprobleem maken. Als u stacktraceringen regelmatig wilt gebruiken, moet uw profiler in plaats daarvan een schaduwstack maken met behulp van de callbacks van de uitzondering FunctionEnter2, FunctionLeave2, FunctionTailcall2 en ICorProfilerCallback2. De schaduwstack is altijd actueel en kan snel naar de opslag worden gekopieerd wanneer een momentopname van een stack nodig is.

Een schaduwstack kan functieargumenten verkrijgen, waarden retourneren en informatie over algemene instantiëringen. Deze informatie is alleen beschikbaar via de schaduwstack en kan worden verkregen wanneer de besturing aan een functie wordt overhandigd. Deze informatie is echter mogelijk niet later beschikbaar tijdens de uitvoering van de functie.

Callbacks en stackdiepte

Profiler callbacks kunnen worden uitgegeven in zeer stack-beperkte omstandigheden, en een stack-overloop in een profiler callback leidt tot een onmiddellijke procesafsluiting. Een profiler moet ervoor zorgen dat er zo weinig mogelijk stack wordt gebruikt als reactie op callbacks. Als de profiler is bedoeld voor gebruik tegen processen die robuust zijn tegen stack overflow, moet de profiler zelf ook voorkomen dat stack-overloop wordt geactiveerd.

Title Beschrijving
Een profileringsomgeving instellen Hierin wordt uitgelegd hoe u een profiler initialiseert, gebeurtenismeldingen instelt en een Windows-service profileert.
Profileringsinterfaces Beschrijft de niet-beheerde interfaces die door de profilerings-API worden gebruikt.
Globale statische functies profileren Beschrijft de niet-beheerde globale statische functies die door de profilerings-API worden gebruikt.
Opsommingen profileren Beschrijft de niet-beheerde opsommingen die door de profilerings-API worden gebruikt.
Profileringsstructuren Beschrijft de niet-beheerde structuren die door de profilerings-API worden gebruikt.