Dela via


Profileringsöversikt

En profilerare är ett verktyg som övervakar körningen av ett annat program. En CLR-profilerare (Common Language Runtime) är ett DLL -bibliotek (Dynamic Link Library) som består av funktioner som tar emot meddelanden från och skickar meddelanden till CLR med hjälp av profilerings-API:et. Profilerar-DLL:en läses in av CLR vid körning.

Traditionella profileringsverktyg fokuserar på att mäta körningen av programmet. De mäter alltså den tid som spenderas i varje funktion eller programmets minnesanvändning över tid. Profilerings-API:et riktar sig mot en bredare klass av diagnostikverktyg, till exempel verktyg för kodtäckning och till och med avancerade felsökningshjälpmedel. Dessa användningsområden är alla diagnostiska till sin natur. Profilerings-API:et mäter inte bara utan övervakar även körningen av ett program. Därför bör profilerings-API:et aldrig användas av själva programmet, och programmets körning bör inte vara beroende av (eller påverkas av) profileraren.

Profilering av ett CLR-program kräver mer stöd än profilering av konventionellt kompilerad datorkod. Detta beror på att CLR introducerar begrepp som programdomäner, skräpinsamling, hanterad undantagshantering, JIT-kompilering (just-in-time) av kod (konverterar vanligt mellanliggande språk eller CIL, kod till inbyggd datorkod) och liknande funktioner. Konventionella profileringsmekanismer kan inte identifiera eller ge användbar information om dessa funktioner. Profilerings-API:et ger den här saknade informationen effektivt, med minimal effekt på prestanda för CLR och det profilerade programmet.

JIT-kompilering vid körning ger goda möjligheter till profilering. Profilerings-API:et gör det möjligt för en profilerare att ändra den minnesinterna CIL-kodströmmen för en rutin innan den är JIT-kompilerad. På så sätt kan profileraren dynamiskt lägga till instrumentationskod i vissa rutiner som behöver undersökas närmare. Även om den här metoden är möjlig i konventionella scenarier är det mycket enklare att implementera för CLR med hjälp av profilerings-API:et.

Profilerings-API:et

Vanligtvis används profilerings-API:et för att skriva en kodprofilerare, vilket är ett program som övervakar körningen av ett hanterat program.

Profilerings-API:et används av en profilerar-DLL som läses in i samma process som det program som profileras. Profiler-DLL implementerar ett motringningsgränssnitt (ICorProfilerCallback i .NET Framework version 1.0 och 1.1, ICorProfilerCallback2 i version 2.0 och senare). CLR anropar metoderna i gränssnittet för att meddela profileraren om händelser i den profilerade processen. Profileraren kan anropa tillbaka till körningen med hjälp av metoderna i gränssnitten ICorProfilerInfo och ICorProfilerInfo2 för att få information om tillståndet för det profilerade programmet.

Kommentar

Endast datainsamlingsdelen av profileringslösningen ska köras i samma process som det profilerade programmet. Alla användargränssnitt och dataanalyser ska utföras i en separat process.

Följande bild visar hur profilerar-DLL:en interagerar med det program som profileras och CLR.

Skärmbild som visar profileringsarkitekturen.

Meddelandegränssnitten

ICorProfilerCallback och ICorProfilerCallback2 kan betraktas som meddelandegränssnitt. Dessa gränssnitt består av metoder som ClassLoadStarted, ClassLoadFinished och JITCompilationStarted. Varje gång CLR läser in eller tar bort en klass, kompilerar en funktion och så vidare anropas motsvarande metod i profilerarens ICorProfilerCallback eller ICorProfilerCallback2 gränssnittet.

En profilerare kan till exempel mäta kodprestanda via två meddelandefunktioner: FunctionEnter2 och FunctionLeave2. Det tidsstämplar bara varje meddelande, ackumulerar resultat och matar ut en lista som anger vilka funktioner som förbrukade mest CPU- eller wall-clock-tid under körningen av programmet.

Gränssnitt för informationshämtning

De andra huvudgränssnitten inom profilering är ICorProfilerInfo och ICorProfilerInfo2. Profileraren anropar dessa gränssnitt efter behov för att få mer information för att hjälpa till med analysen. När CLR till exempel anropar funktionen FunctionEnter2 tillhandahåller den en funktionsidentifierare. Profileraren kan få mer information om funktionen genom att anropa metoden ICorProfilerInfo2::GetFunctionInfo2 för att identifiera funktionens överordnade klass, dess namn och så vidare.

Funktioner som stöds

Profilerings-API:et innehåller information om en mängd olika händelser och åtgärder som inträffar i den gemensamma språkkörningen. Du kan använda den här informationen för att övervaka de inre processerna och analysera prestanda för ditt .NET Framework-program.

Profilerings-API:et hämtar information om följande åtgärder och händelser som inträffar i CLR:

  • CLR-start- och avstängningshändelser.

  • Händelser för att skapa och stänga av programdomäner.

  • Händelser för monteringsinläsning och avlastning.

  • Inläsnings- och avlastningshändelser för moduler.

  • COM vtable creation and destruction events.COM vtable creation and destruction events.

  • Jit-kompilerings- och kod pitching-händelser (just-in-time).

  • Händelser för klassinläsning och avlastning.

  • Skapa och förstöra trådar.

  • Funktionsinmatnings- och avslutningshändelser.

  • Undantag.

  • Övergångar mellan hanterad och ohanterad kodkörning.

  • Övergångar mellan olika körningskontexter.

  • Information om körningsavstängningar.

  • Information om körningsminneshögen och skräpinsamlingsaktiviteten.

Profilerings-API:et kan anropas från valfritt (icke-hanterat) COM-kompatibelt språk.

API:et är effektivt när det gäller processor- och minnesförbrukning. Profilering omfattar inte ändringar i det profilerade programmet som är tillräckligt betydande för att orsaka missvisande resultat.

Profilerings-API:et är användbart för både sampling och profilerare som inte samplar. En samplingsprofiler inspekterar profilen vid vanliga klockfästningar, till exempel vid 5 millisekunders mellanrum. En profilerare utan sampling informeras om en händelse synkront med den tråd som orsakar händelsen.

Funktioner som inte stöds

Profilerings-API:et stöder inte följande funktioner:

  • Ohanterad kod, som måste profileras med konventionella Win32-metoder. CLR-profileraren innehåller dock övergångshändelser för att fastställa gränserna mellan hanterad och ohanterad kod.

  • Självredigerande program som ändrar sin egen kod för ändamål som aspektorienterad programmering.

  • Gränskontroll, eftersom profilerings-API:et inte tillhandahåller den här informationen. CLR ger inbyggt stöd för gränskontroll av all hanterad kod.

  • Fjärrprofilering, vilket inte stöds av följande skäl:

    • Fjärrprofilering förlänger körningstiden. När du använder profileringsgränssnitten måste du minimera körningstiden så att profileringsresultaten inte påverkas i onödan. Detta gäller särskilt när körningsprestanda övervakas. Fjärrprofilering är dock inte en begränsning när profileringsgränssnitten används för att övervaka minnesanvändning eller för att hämta körningsinformation om stackramar, objekt och så vidare.

    • CLR-kodprofileraren måste registrera ett eller flera motringningsgränssnitt med körningen på den lokala dator där det profilerade programmet körs. Detta begränsar möjligheten att skapa en fjärransluten kodprofilerare.

Meddelandetrådar

I de flesta fall kör tråden som genererar en händelse också meddelanden. Sådana meddelanden (till exempel FunctionEnter och FunctionLeave) behöver inte ange den explicita ThreadID. Profileraren kan också välja att använda trådlokal lagring för att lagra och uppdatera sina analysblock i stället för att indexera analysblocken i den globala lagringen, baserat på den berörda trådens ThreadID .

Observera att dessa återanrop inte serialiseras. Användarna måste skydda sin kod genom att skapa trådsäkra datastrukturer och genom att låsa profileringskoden vid behov för att förhindra parallell åtkomst från flera trådar. I vissa fall kan du därför få en ovanlig sekvens med återanrop. Anta till exempel att ett hanterat program skapar två trådar som kör identisk kod. I det här fallet är det möjligt att ta emot en ICorProfilerCallback::JITCompilationStarted-händelse för någon funktion från en tråd och ett FunctionEnter återanrop från den andra tråden innan du tar emot ICorProfilerCallback::JITCompilationFinished callback. I det här fallet får användaren ett FunctionEnter återanrop för en funktion som kanske inte har kompilerats helt just-in-time (JIT) ännu.

Säkerhet

En profilerar-DLL är en ohanterad DLL som körs som en del av körningsmotorn för common language runtime. Det innebär att koden i profilerar-DLL:en inte omfattas av begränsningarna för åtkomstsäkerhet för hanterad kod. De enda begränsningarna för profilerar-DLL:en är de som åläggs av operativsystemet för den användare som kör det profilerade programmet.

Profilerare bör vidta lämpliga försiktighetsåtgärder för att undvika säkerhetsrelaterade problem. Under installationen bör till exempel en profilerar-DLL läggas till i en åtkomstkontrollista (ACL) så att en obehörig användare inte kan ändra den.

Kombinera hanterad och ohanterad kod i en kodprofilerare

En felaktigt skriven profilerare kan orsaka cirkelreferenser till sig själv, vilket resulterar i oförutsägbart beteende.

En granskning av CLR-profilerings-API:et kan ge intryck av att du kan skriva en profilerare som innehåller hanterade och ohanterade komponenter som anropar varandra via COM-interop eller indirekta anrop.

Även om detta är möjligt ur ett designperspektiv stöder profilerings-API:et inte hanterade komponenter. En CLR-profilerare måste vara helt ohanterad. Försök att kombinera hanterad och ohanterad kod i en CLR-profilerare kan orsaka åtkomstöverträdelser, programfel eller dödlägen. Profilerarens hanterade komponenter utlöser händelser tillbaka till sina ohanterade komponenter, vilket senare anropar de hanterade komponenterna igen, vilket resulterar i cirkelreferenser.

Den enda plats där en CLR-profilerare kan anropa hanterad kod på ett säkert sätt är i CIL-brödtexten (Common Intermediate Language) för en metod. Den rekommenderade metoden för att ändra CIL-brödtexten är att använda JIT-omkompileringsmetoderna i gränssnittet ICorProfilerCallback4 .

Det är också möjligt att använda de äldre instrumenteringsmetoderna för att ändra CIL. Innan jit-kompilering av en funktion har slutförts kan profileraren infoga hanterade anrop i CIL-brödtexten för en metod och sedan JIT-kompilera den (se metoden ICorProfilerInfo::GetILFunctionBody ). Den här tekniken kan användas för selektiv instrumentering av hanterad kod eller för att samla in statistik och prestandadata om JIT.

Alternativt kan en kodprofilerare infoga inbyggda krokar i CIL-brödtexten för varje hanterad funktion som anropar till ohanterad kod. Den här tekniken kan användas för instrumentation och täckning. En kodprofilerare kan till exempel infoga instrumentationskrokar efter varje CIL-block för att säkerställa att blocket har körts. Ändringen av CIL-kroppen av en metod är en mycket känslig åtgärd, och det finns många faktorer som bör beaktas.

Profilering av ohanterad kod

CLR-profilerings-API:et (Common Language Runtime) ger minimalt stöd för profilering av ohanterad kod. Följande funktioner tillhandahålls:

  • Uppräkning av stackkedjor. Med den här funktionen kan en kodprofilerare fastställa gränsen mellan hanterad kod och ohanterad kod.

  • Avgöra om en stackkedja motsvarar hanterad kod eller intern kod.

I .NET Framework-versionerna 1.0 och 1.1 är dessa metoder tillgängliga via den pågående delmängden av CLR-felsöknings-API:et. De definieras i filen CorDebug.idl.

I .NET Framework 2.0 och senare kan du använda metoden ICorProfilerInfo2::D oStackSnapshot för den här funktionen.

Använda COM

Även om profileringsgränssnitten definieras som COM-gränssnitt initierar inte CLR (Common Language Runtime) COM för att använda dessa gränssnitt. Anledningen är att undvika att behöva ange trådningsmodellen med hjälp av funktionen CoInitialize innan det hanterade programmet har haft möjlighet att ange önskad trådningsmodell. På samma sätt bör profileraren själv inte anropa CoInitialize, eftersom den kan välja en trådmodell som inte är kompatibel med att programmet profileras och kan orsaka att programmet misslyckas.

Anropsstackar

Profilerings-API:et tillhandahåller två sätt att hämta anropsstackar: en stack-ögonblicksbildsmetod som möjliggör gles insamling av anropsstackar och en skuggstackmetod som spårar anropsstacken i varje ögonblick.

Stackögonblicksbild

En stackögonblicksbild är en spårning av stacken i en tråd direkt i tiden. Profilerings-API:et stöder spårning av hanterade funktioner på stacken, men det lämnar spårningen av ohanterade funktioner till profilerarens egen stack-walker.

Mer information om hur du programmerar profileraren att gå igenom hanterade staplar finns i ICorProfilerInfo2::D oStackSnapshot-metoden i den här dokumentationsuppsättningen och Profiler Stack Walking i .NET Framework 2.0: Basics and Beyond.

Skuggstack

Om du använder metoden för ögonblicksbilder för ofta kan du snabbt skapa ett prestandaproblem. Om du vill ta stackspårningar ofta bör profileraren i stället skapa en skuggstack med hjälp av återanropen FunctionEnter2, FunctionLeave2, FunctionTailcall2 och ICorProfilerCallback2. Skuggstacken är alltid aktuell och kan snabbt kopieras till lagring när en stackögonblicksbild behövs.

En skuggstack kan hämta funktionsargument, returvärden och information om allmänna instansier. Den här informationen är endast tillgänglig via skuggstacken och kan hämtas när kontrollen överlämnas till en funktion. Den här informationen kanske dock inte är tillgänglig senare under körningen av funktionen.

Återanrop och stackdjup

Profiler-återanrop kan utfärdas under mycket stackbegränsade omständigheter och ett stackspill i ett profileraråteranrop leder till att processen avslutas omedelbart. En profilerare bör se till att använda så lite stack som möjligt som svar på återanrop. Om profileraren är avsedd för användning mot processer som är robusta mot stackspill bör profileraren själv också undvika att utlösa stackspill.

Title Description
Konfigurera en profileringsmiljö Förklarar hur du initierar en profilerare, anger händelsemeddelanden och profilerar en Windows-tjänst.
Profileringsgränssnitt Beskriver de ohanterade gränssnitt som profilerings-API:et använder.
Profilering av globala statiska funktioner Beskriver de ohanterade globala statiska funktioner som profilerings-API:et använder.
Profileringsuppräkningar Beskriver de ohanterade uppräkningar som profilerings-API:et använder.
Profileringsstrukturer Beskriver de ohanterade strukturer som profilerings-API:et använder.