.NET ist eine verwaltete Laufzeit . Dies bedeutet, dass es übergeordnete Funktionen enthält, die Ihr Programm für Sie steuern (ab Einführung in die Common Language Runtime (CLR), 2007 ):
Die Laufzeit bietet viele Funktionen, daher ist es praktisch, sie in die folgenden Kategorien zu unterteilen:
- Die Hauptfunktionen , die das Gerät anderer betreffen. Dazu gehören:
- Müllabfuhr;
- Sicherung des Speicherzugriffs und Typensystemsicherheit;
- Unterstützung auf hohem Niveau für Programmiersprachen.
- Zusätzliche Funktionen - arbeiten auf der Basis der Hauptfunktionen. Viele nützliche Programme verzichten darauf. Diese Funktionen umfassen:
- Anwendungen mit AppDomains isolieren
- Anwendungsschutz und Sandbox-Isolierung.
- Andere Funktionen werden von allen Laufzeiten benötigt, verwenden jedoch nicht die grundlegenden CLR-Funktionen. Solche Funktionen spiegeln den Wunsch wider, eine vollständige Programmierumgebung zu erstellen. Dazu gehören:
- Versionskontrolle;
- Debuggen / Profiling;
- Interaktion sicherstellen.
Es ist ersichtlich, dass Debugging und Profiling zwar nicht die Haupt- oder Zusatzfunktionen sind, sie jedoch auf der Liste stehen, weil " eine vollwertige Programmierumgebung erstellt werden soll ".

Der Rest des Beitrags beschreibt, welche Überwachungs- , Beobachtbarkeits- und Introspektionsfunktionen in der Core CLR vorhanden sind, warum sie nützlich sind und wie die Umgebung sie bereitstellt.
Diagnose
Schauen Sie sich zunächst die Diagnoseinformationen an, die uns die CLR zur Verfügung stellt. Traditionell wurde hierfür die Ereignisverfolgung für Windows (ETW) verwendet.
Es gibt viele Ereignisse, über die die CLR Informationen bereitstellt. Sie sind verbunden mit:
- Müllabfuhr (GC);
- JIT-Zusammenstellung;
- Module und Anwendungsdomänen;
- beim Blockieren mit Threads und Konflikten arbeiten;
- sowie viele andere.
Hier tritt beispielsweise ein Ereignis beim Laden in AppDomain auf , hier ist das Ereignis mit dem Auslösen einer Ausnahme verbunden und hier mit dem Speicherzuweisungszyklus des Garbage Collectors .
Perf view
Wenn Sie die Ereignisse im Trace-System (ETW) anzeigen möchten, die sich auf Ihre .NET-Anwendungen beziehen, empfehle ich die Verwendung des hervorragenden PerfView-Tools und beginnen Sie mit diesen Schulungsvideos oder dieser Präsentation von PerfView: The Ultimate .NET Performance Tool . PerfView ist weithin dafür bekannt, wertvolle Informationen bereitzustellen. Beispielsweise verwenden Microsoft-Ingenieure es regelmäßig, um die Leistung zu analysieren .
Gemeinsame Infrastruktur
Wenn der Name plötzlich nicht mehr klar ist, ist die Ereignisverfolgung in ETW nur unter Windows verfügbar, was nicht sehr gut zur plattformübergreifenden Welt von .NET Core passt. Mit PerfView können Sie die Leistung unter Linux analysieren (mit LTTng). Dieses Befehlszeilentool namens PerfCollect sammelt jedoch nur Daten. Analysefunktionen und eine umfangreiche Benutzeroberfläche (einschließlich Flammengraphen ) sind derzeit nur in Windows-Lösungen verfügbar.
Wenn Sie jedoch die .NET-Leistung unter Linux analysieren möchten, gibt es andere Ansätze:
Der zweite Link oben führt zu einer Diskussion der neuen EventPipe-Infrastruktur , an der in .NET Core (zusätzlich zu EventSources & EventListeners) gearbeitet wird. Die Entwicklungsziele finden Sie im plattformübergreifenden Designdokument zur Leistungsüberwachung . Auf hoher Ebene wird durch diese Infrastruktur ein einziger Ort geschaffen, an dem die CLR Ereignisse im Zusammenhang mit Diagnose und Leistung sendet. Diese Ereignisse werden dann an einen oder mehrere Protokollierer umgeleitet, zu denen beispielsweise ETW, LTTng und BPF gehören können. Der erforderliche Logger wird abhängig vom Betriebssystem oder der Plattform ermittelt, auf der die CLR ausgeführt wird. Eine ausführliche Erläuterung der Vor- und Nachteile verschiedener Protokollierungstechnologien finden Sie im plattformübergreifenden .NET-Leistungs- und Eventing-Design .
Der Fortschritt von EventPipes wird über das Leistungsüberwachungsprojekt und die damit verbundenen 'EventPipe'-Probleme überwacht.
Zukunftspläne
Schließlich ist geplant, einen Performance Profiling Controller zu erstellen, der die folgenden Aufgaben hat:
Der Controller muss die Profiling-Infrastruktur verwalten und Leistungsdaten, die von .NET-Komponenten generiert werden, die für die Leistungsdiagnose verantwortlich sind, auf einfache und plattformübergreifende Weise präsentieren.
Gemäß dem Plan sollte der Controller die folgenden Funktionen über den HTTP-Server bereitstellen und alle erforderlichen Daten von der EventPipes-Infrastruktur empfangen:
REST-APIs
- Prinzip 1: Einfache Profilerstellung: Profilieren Sie die Laufzeit über den Zeitraum X und geben Sie die Ablaufverfolgung zurück.
- Prinzip 1: Erweiterte Profilerstellung: Starten Sie die Verfolgung (zusammen mit der Konfiguration).
- Prinzip 1: Erweiterte Profilerstellung: Vollständige Nachverfolgung (die Antwort auf diesen Aufruf ist die Ablaufverfolgung selbst).
- Prinzip 2: Abrufen von Statistiken zu allen EventCountern oder einem bestimmten EventCounter.
HTML-durchsuchbare Seiten
- Prinzip 1: Eine Textdarstellung aller Stapel von verwaltetem Code in einem Prozess.
- Erstellt Snapshots laufender Prozesse zur Verwendung als einfacher Diagnosebericht.
- Prinzip 2: Anzeigen des aktuellen Status (möglicherweise mit einem Verlauf) der EventCounters-Zähler.
- Bietet einen Überblick über vorhandene Zähler und deren Werte.
- UNGELÖSTES PROBLEM: Ich glaube nicht, dass es öffentliche APIs gibt, um EventCounters zu zählen.
Ich möchte wirklich sehen, was mit dem Performance Profiling Controller (PPC?) Passiert. Ich denke, wenn es in die CLR integriert ist, bringt es .NET viele Vorteile. Diese Funktionalität existiert in anderen Laufzeiten .
Profilerstellung
Ein weiteres effektives Tool der CLR ist eine Profiling-API. Es wird (hauptsächlich) von Tools von Drittanbietern verwendet, um auf niedriger Ebene eine Verbindung zur Laufzeit herzustellen. In dieser Überprüfung erfahren Sie mehr über die API. Auf hoher Ebene können Sie sie jedoch verwenden, um Rückrufe zu tätigen, die aktiviert werden, wenn:
- Ereignisse im Zusammenhang mit dem Müllsammler;
- Ausnahmen werden geworfen;
- Baugruppen werden geladen / entladen;
- und vieles mehr .
Bild von der BOTR Profiling API-Seite - Übersicht
Darüber hinaus verfügt es über weitere effektive Funktionen. Zunächst können Sie Handler einrichten, die bei jeder Ausführung der .NET-Methode aufgerufen werden, entweder in der Umgebung selbst oder über Benutzercode. Diese Rückrufe werden als Enter / Leave-Handler bezeichnet. Hier ist ein gutes Beispiel für deren Verwendung. Dazu müssen Sie jedoch die Aufrufkonventionen für verschiedene Betriebssystem- und CPU-Architekturen verstehen, was nicht immer einfach ist . Denken Sie auch daran, dass die Profiling-API eine COM-Komponente ist, auf die nur über C / C ++ - Code zugegriffen werden kann, nicht jedoch über C # / F # / VB.NET.
Zweitens kann der Profiler den IL-Code einer beliebigen .NET-Methode vor der JIT-Kompilierung mithilfe der SetILFunctionBody () -API neu schreiben . Diese API ist wirklich effizient. Es liegt vielen APM .NET-Tools zugrunde . Weitere Informationen zu seiner Verwendung finden Sie in meinem Beitrag Wie man versiegelte Klassen, statische Methoden und verwandten Code verspottet .
ICorProfiler API
Es stellt sich heraus, dass die Profiling-API funktioniert hat. In der Laufzeitumgebung sollten alle möglichen Tricks vorhanden sein. Schauen Sie sich einfach die Diskussion auf der Seite " Rejit zulassen" im Anhang an (weitere Informationen zu ReJIT finden Sie unter ReJIT: Eine Anleitung).
Eine vollständige Definition aller Profiling-API-Schnittstellen und -Rückrufe finden Sie in \ vm \ inc \ corprof.idl (siehe Sprache für die Schnittstellenbeschreibung ). Es ist in 2 logische Teile unterteilt. Ein Teil ist die EE- Schnittstelle (Profiler -> Runtime Environment) , bekannt als ICorProfilerInfo
:
// , ICorProfilerInfo*, // . , DLL // , // .
Dies ist in den folgenden Dateien implementiert:
Der andere Hauptteil sind Rückrufe Runtime -> Profiler, die unter der Schnittstelle ICorProfilerCallback
:
// // ICorProfilerCallaback* . // , EEToProfInterfaceImpl.
Diese Rückrufe werden in den folgenden Dateien implementiert:
Schließlich ist anzumerken, dass Profiling-APIs möglicherweise nicht auf allen Betriebssystemen und Architekturen funktionieren, auf denen .NET Core ausgeführt wird. Hier ein Beispiel: Probleme mit ELT-Aufrufstubs unter Linux . Weitere Informationen finden Sie im Status der CoreCLR Profiler-APIs .
Profilerstellung v. Debuggen
Als kleinen Exkurs muss ich sagen, dass sich Profiling und Debugging immer noch ein wenig überschneiden. Daher ist es hilfreich zu verstehen, was verschiedene APIs im Kontext der .NET-Laufzeit bieten (entnommen aus CLR-Debugging vs. CLR-Profilerstellung ).
Der Unterschied zwischen Debugging und Profiling in der CLR
Debuggen
Entwickler verstehen das Debuggen anders. Zum Beispiel habe ich auf Twitter gefragt, wie man .NET-Programme debuggt, und viele verschiedene Antworten erhalten . Gleichzeitig enthielten die Antworten eine gute Liste von Werkzeugen und Methoden, daher empfehle ich, sie sich anzusehen. Vielen Dank, dass Sie #LazyWeb
Ich denke, dass das Beste am Debuggen diese Botschaft widerspiegelt:
Die CLR bietet eine umfangreiche Liste von Debugging-Funktionen. Warum werden diese Mittel jedoch benötigt? In diesem großartigen Beitrag werden mindestens drei Gründe genannt. Warum unterscheidet sich das verwaltete Debuggen vom nativen Debuggen? ::
- Das Debuggen von nicht verwaltetem Code kann auf Hardwareebene abstrahiert werden, das Debuggen von verwaltetem Code muss jedoch auf IL-Codeebene abstrahiert werden.
- Das Debuggen von verwaltetem Code erfordert viele Informationen, die vor der Ausführung nicht verfügbar sind.
- Der Managed Code Debugger muss sich mit dem Garbage Collector (GC) abstimmen.
Aus ICorDebug
Benutzerfreundlichkeit sollte die CLR daher eine Debugging-API auf hoher Ebene bereitstellen, die als ICorDebug
. Die folgende Abbildung zeigt ein allgemeines Debugging-Szenario (Quelle: BOTR):
ICorDebug API
Das Implementierungsprinzip und die Beschreibung der verschiedenen Komponenten stammen aus dem CLR-Debugging, einer kurzen Einführung :
Die gesamte Debugging-Unterstützung in .Net wird über der DLL-Bibliothek implementiert, die wir The Dac nennen. Diese Datei (normalerweise mscordacwks.dll
) ist ein Strukturelement sowohl für unsere öffentliche Debugging-API ( ICorDebug
) als auch für zwei private Debugging-APIs: SOS-Dac-API und IXCLR.
In einer idealen Welt würde jeder ICorDebug
, unsere öffentliche API, verwenden. ICorDebug
fehlen jedoch viele der Funktionen, die Tool-Entwickler benötigen. Dies ist das Problem, das wir zu beheben versuchen, wo wir können. Diese Verbesserungen sind jedoch nur in der CL.v.next vorhanden, nicht jedoch in früheren Versionen der CLR. Tatsächlich wurde die Unterstützung für das Debuggen von ICorDebug
in der ICorDebug
API erst mit der Veröffentlichung von CLR v4 ICorDebug
. Jeder, der Crash-Dumps zum Debuggen in CLR v2 verwendet, kann ICorDebug
überhaupt nicht anwenden.
(Weitere Informationen finden Sie unter SOS & ICorDebug.)
Tatsächlich ist die ICorDebug
API in mehr als 70 Schnittstellen unterteilt. Ich werde ihnen nicht alle geben, aber ich werde zeigen, nach welchen Kategorien sie unterteilt werden können. Weitere Informationen finden Sie in der Partition von ICorDebug, in der diese Liste veröffentlicht wurde.
- Oberste Ebene : ICorDebug + ICorDebug2 - Schnittstellen der obersten Ebene, die perfekt als Sammlung von ICorDebugProcess-Objekten dienen.
- Rückrufe : Debugging-Ereignisse für verwalteten Code werden über Methoden an das vom Debugger implementierte Rückrufobjekt gesendet.
- Prozess : Dieser Satz von Schnittstellen stellt Arbeitscode dar und enthält ereignisbezogene APIs.
- Code- / Typprüfung : Funktioniert hauptsächlich mit statischen PE-Bildern, es gibt jedoch bequeme Methoden für reale Daten.
- Ausführungskontrolle : Möglichkeit, den Fortschritt des Threads zu überwachen. In der Praxis bedeutet dies die Möglichkeit, Haltepunkte (F9) festzulegen und den Code zu durchlaufen (F11-Codeeingabe, F10-Code-Bypass, S + F11-Code-Exit). Die ICorDebug-Ausführungssteuerungsfunktion funktioniert nur in verwaltetem Code.
- Threads + Call Stacks : Call Stacks sind die Basis für die vom Debugger implementierten Inspektionsfunktionen. Die Arbeit mit dem Aufrufstapel erfolgt über die folgenden Schnittstellen. ICorDebug unterstützt nur das Debuggen von verwaltetem Code. Dementsprechend können Sie den Stapel nur von verwaltetem Code verfolgen.
- Objektinspektion : Die Objektinspektion ist Teil der API, mit der Sie die Werte von Variablen im debuggten Code anzeigen können. Für jede Schnittstelle gebe ich die MVP-Methode an, die meines Erachtens den Zweck dieser Schnittstelle kurz beschreiben sollte.
Wie bei Profiling-APIs variieren die Unterstützungsstufen der Debugging-API je nach Betriebssystem und Prozessorarchitektur. Zum Beispiel gibt es ab August 2018 noch keine Linux ARM-Lösung zum Diagnostizieren und Debuggen von verwaltetem Code. Weitere Informationen zur Linux-Unterstützung finden Sie unter Debuggen von .NET Core unter Linux mit LLDB und im Diagnose-Repository von Microsoft, um das Debuggen von .NET-Programmen unter Linux zu vereinfachen.
Wenn Sie sehen möchten, wie die ICorDebug
API in C # aussieht, sehen Sie sich die Wrapper in der CLRMD-Bibliothek an, einschließlich aller verfügbaren Rückrufe (mehr zu CLRMD wird später in diesem Beitrag erläutert).
SOS und DAC
Die Datenzugriffskomponente (DAC) wird auf der BOTR-Seite ausführlich erläutert . Im Wesentlichen bietet es Zugriff außerhalb des Prozesses auf die CLR-Datenstrukturen, sodass die darin enthaltenen Informationen aus einem anderen Prozess gelesen werden können. So kann der Debugger (über ICorDebug
) oder die SOS-Erweiterung (Son of Strike) auf die laufende CLR-Instanz oder den Speicherauszug zugreifen und beispielsweise ICorDebug
suchen:
- alle laufenden Threads;
- verwaltete Heap-Objekte
- vollständige Informationen über die Methode, einschließlich Maschinencode;
- Aktuelle Stapelverfolgung.
Ein kleiner Exkurs : Wenn Sie herausfinden möchten, woher diese seltsamen Namen stammen, und eine kleine Lektion in der .NET-Geschichte erhalten möchten, lesen Sie diese Antwort unter Stapelüberlauf .
Die vollständige Liste der SOS- Befehle ist beeindruckend . Wenn Sie es zusammen mit WinDBG verwenden, können Sie auf sehr niedrigem Niveau herausfinden, was in Ihrem Programm und in der CLR passiert. Um zu sehen, wie alles implementiert ist, schauen wir uns den !HeapStat
, der eine Beschreibung der Größen der verschiedenen Heaps anzeigt, die der .NET GC verwendet:
(Bild aus SOS: Die kommende Version enthält einige neue Befehle - HeapStat)
Hier ist ein Code-Stream, der zeigt, wie SOS und DAC zusammenarbeiten:
- SOS Complete Team
!HeapStat
( Link ) - SOS- Code im
!HeapStat
, der mit Workstation GC funktioniert (Link) - SOS- Funktion
GCHeapUsageStats(..)
, die den schwierigsten Teil der Arbeit ausführt ( Link ) - Shared
DacpGcHeapDetails
Datenstruktur, die Zeiger auf die Hauptdaten im GC-Heap enthält, z. B. Segmente, Bitmasken und einzelne Generationen ( Referenz ). - DAC
GetGCHeapStaticData
Funktion, die die DacpGcHeapDetails
Struktur ausfüllt ( Link ) - Gemeinsame
DacpHeapSegmentData
Datenstruktur, die Informationen zu einem einzelnen GC-Heap-Segment enthält ( Link ) - DAC
GetHeapSegmentData(..)
, das die DacpHeapSegmentData
Struktur DacpHeapSegmentData
( Link )
Debugger von Drittanbietern
Seit Microsoft die Debugging-API veröffentlicht hat, konnten Entwickler von ICorDebug
die ICorDebug
Schnittstellen verwenden. Hier ist eine Liste derjenigen, die ich gefunden habe:
Speicherabbilder
Das Letzte, worüber wir sprechen werden, sind Speicherabbilder, die von einem funktionierenden System abgerufen und außerhalb analysiert werden können. Die .NET-Laufzeit unterstützt immer das Speichern von Speicher unter Windows . Und jetzt, da .NET Core plattformübergreifend geworden ist, sind Tools erschienen, die dieselbe Aufgabe auf anderen Betriebssystemen ausführen.
Bei der Verwendung von Speicherabbildern ist es manchmal schwierig, die richtigen, übereinstimmenden Versionen von SOS- und DAC-Dateien zu erhalten. Glücklicherweise hat Microsoft kürzlich das dotnet symbol
CLI-Tool veröffentlicht, das:
kann alle zum Debuggen erforderlichen Dateien (Zeichensätze, Module, SOS- und DAC-Dateien für ein bestimmtes Coreclr-Modul) für einen bestimmten Core-Dump, Minidump oder Dateien einer unterstützten Plattform herunterladen, einschließlich ELF, MachO, Windows DLL, PDB und Portable PDB
Wenn Sie ein wenig die Speicherauszüge analysieren möchten, empfehlen wir Ihnen einen Blick auf die hervorragende CLR-MD-Bibliothek, die Microsoft vor einigen Jahren veröffentlicht hat. Ich habe bereits über seine Funktionen geschrieben. Kurz gesagt, mithilfe der Bibliothek können Sie mit Speicherabbildern über eine intuitive C # -API arbeiten, die Klassen enthält, die Zugriff auf ClrHeap, GC-Roots, CLR-Threads, Stapelrahmen und vieles mehr bieten. Tatsächlich kann die CLR-MD die meisten (wenn nicht alle) SOS-Befehle implementieren.
Wie es funktioniert, erfahren Sie in diesem Beitrag :
Die verwaltete ClrMD-Bibliothek ist ein Wrapper für Debugging-APIs, die nur für den internen Gebrauch in der CLR vorgesehen sind. Trotz der Tatsache, dass diese APIs für die Diagnose sehr effektiv sind, unterstützen wir sie nicht in Form von öffentlichen, dokumentierten Releases, da ihre Verwendung komplex ist und in engem Zusammenhang mit anderen Funktionen der CLR-Implementierung steht. ClrMD löst dieses Problem, indem es einen benutzerfreundlichen, verwaltbaren Wrapper für diese Low-Level-Debugging-APIs bereitstellt.
