Diagnoseports

Dieser Artikel gilt für: ✔️ .NET Core 3.1 oder höher

Die .NET-Runtime macht einen Dienstendpunkt verfügbar, der anderen Prozessen das Senden von Diagnosebefehlen und das Empfangen von Antworten über einen IPC-Kanal ermöglicht. Dieser Endpunkt wird als Diagnoseport bezeichnet. Befehle können zu folgenden Zwecken an den Diagnoseport gesendet werden:

  • Erfassen eines Speicherabbilds
  • Starten einer Ablaufverfolgung vom Typ EventPipe
  • Fordern Sie die zum Starten der App verwendete Befehlszeile an.

Der Diagnoseport unterstützt je nach Plattform verschiedene Transporte. Derzeit verwendet sowohl die CoreCLR- als auch die Mono-Runtime-Implementierung Named Pipes unter Windows bzw. Unix Domain Sockets unter Linux und macOS. Die Mono-Runtime-Implementierung unter Android, iOS und tvOS verwendet TCP/IP. Der Kanal verwendet ein benutzerdefiniertes binäres Protokoll. Die meisten Entwickler*innen interagieren nie direkt mit dem zugrunde liegenden Kanal und Protokoll, sondern verwenden GUI- oder CLI-Tools, die in ihrem Auftrag kommunizieren. Beispielsweise abstrahieren die Tools dotnet-dump und dotnet-trace das Senden von Protokollbefehlen, um Speicherabbilder zu erfassen und Ablaufverfolgungen zu starten. Für Entwickler*innen, die benutzerdefinierte Tools schreiben möchten, stellt das NuGet-Paket „Microsoft.Diagnostics.NETCore.Client“ eine .NET-API-Abstraktion des zugrunde liegenden Transports und Protokolls bereit.

Sicherheitsüberlegungen

Der Diagnoseport macht vertrauliche Informationen zu einer ausgeführten Anwendung verfügbar. Wenn nicht vertrauenswürdige Benutzer*innen Zugriff auf diesen Kanal erhalten, können sie unter Umständen Details zum Programmzustand anzeigen (einschließlich ggf. im Arbeitsspeicher befindlicher Geheimnisse) sowie die Ausführung des Programms willkürlich ändern. In der CoreCLR-Runtime ist der Standarddiagnoseport so konfiguriert, dass nur von dem Benutzerkonto, das die App gestartet hat, oder von einem Konto mit Administratorberechtigungen darauf zugegriffen werden kann. Wenn Ihr Sicherheitsmodell anderen Prozessen mit den gleichen Anmeldeinformationen für das Benutzerkonto nicht vertraut, können Sie alle Diagnoseports durch Festlegen der Umgebungsvariablen DOTNET_EnableDiagnostics=0 deaktivieren. Diese Einstellung verhindert, dass Sie externe Tools wie das .NET-Debugging oder eines der Diagnosetools vom Typ „dotnet-*“ verwenden können.

Hinweis

In .NET 6 ist das Präfix DOTNET_ statt COMPlus_ Standard für Umgebungsvariablen, die das .NET-Runtimeverhalten konfigurieren. Das Präfix COMPlus_ funktioniert jedoch weiterhin. Wenn Sie eine frühere Version der .NET-Runtime verwenden, sollten Sie weiterhin das Präfix COMPlus_ für Umgebungsvariablen verwenden.

Standarddiagnoseport

Unter Windows, Linux und macOS ist für die Runtime standardmäßig ein Diagnoseport an einem bekannten Endpunkt geöffnet. Das ist der Port, mit dem die Diagnosetools vom Typ „dotnet-*“ automatisch eine Verbindung herstellen, wenn sie nicht explizit für die Verwendung eines alternativen Ports konfiguriert wurden. Der Endpunkt ist:

  • Windows: Named Pipe \\.\pipe\dotnet-diagnostic-{pid}
  • Linux und macOS: Unix Domain Socket {temp}/dotnet-diagnostic-{pid}-{disambiguation_key}-socket

{pid} ist die Prozess-ID in Dezimalschreibweise, {temp} ist die Umgebungsvariable TMPDIR (oder der Wert /tmp, wenn TMPDIR nicht definiert/leer ist), und {disambiguation_key} ist die Prozessstartzeit in Dezimalschreibweise. Unter macOS und NetBSD entspricht die Prozessstartzeit der Anzahl von Sekunden seit der UNIX-Epochenzeit. Auf allen anderen Plattformen sind es Jiffies seit der Startzeit.

Anhalten der Laufzeit beim Start

Standardmäßig führt die Runtime verwalteten Code nach dem Start aus, unabhängig davon, ob Diagnosetools mit dem Diagnoseport verbunden sind. Manchmal ist es sinnvoll, mit der Ausführung von verwaltetem Code zu warten, bis ein Diagnosetool verbunden ist, um das anfängliche Programmverhalten zu beobachten. Das Festlegen der Umgebungsvariablen DOTNET_DefaultDiagnosticPortSuspend=1 bewirkt, dass die Runtime wartet, bis ein Tool eine Verbindung mit dem Standardport herstellt. Ist nach einigen Sekunden noch kein Tool angefügt, gibt die Runtime in der Konsole eine Warnmeldung mit dem Hinweis aus, in dass immer noch auf das Anfügen eines Tools gewartet wird.

Konfigurieren zusätzlicher Diagnoseports

Hinweis

Das funktioniert nur bei Anwendungen mit .NET 5 oder höher.

Sowohl die Mono-Runtime als auch die CoreCLR-Runtime kann benutzerdefinierte konfigurierte Diagnoseports in der connect-Rolle verwenden. Mono unterstützt auch benutzerdefinierte TCP/IP-Ports in der listen-Rolle, wenn sie mit dotnet-dsrouter unter Android oder iOS verwendet werden. Diese benutzerdefinierten Ports sind zusätzlich zum Standardport vorhanden, der weiterhin verfügbar bleibt. Es gibt einige häufige Gründe, warum benutzerdefinierte Ports nützlich sind:

  • Unter Android, iOS und tvOS gibt es keinen Standardport. Daher muss ein Port konfiguriert werden, um Diagnosetools verwenden zu können.
  • In Umgebungen mit Containern oder Firewalls empfiehlt sich ggf. die Einrichtung einer planbaren Endpunktadresse, die im Gegensatz zum Standardport nicht abhängig von der Prozess-ID variiert. Der benutzerdefinierte Port kann dann explizit einer Zulassungsliste hinzugefügt oder mithilfe eines Proxys über eine Sicherheitsgrenze hinweg verwendet werden.
  • Für Überwachungstools ist es hilfreich, wenn das Tool an einem Endpunkt lauscht und die Runtime aktiv versucht, eine Verbindung damit herzustellen. Dadurch muss das Überwachungstool nicht kontinuierlich Abfragen ausführen, um zu prüfen, ob neue Apps gestartet wurden. In Umgebungen, in denen nicht auf den Standarddiagnoseport zugegriffen werden kann, wird dadurch außerdem vermieden, dass der Monitor mit einem benutzerdefinierten Endpunkt für jede überwachte App konfiguriert werden muss.

In jedem Kommunikationskanal zwischen einem Diagnosetool und der .NET-Runtime muss eine Seite als Listener fungieren und warten, bis die andere Seite eine Verbindung herstellt. Die Laufzeitumgebung kann für jeden Port in der connect-Rolle konfiguriert werden. (Die Mono-Laufzeit kann auch so konfiguriert werden, dass sie für jeden Port in der listen-Rolle agiert.) Ports können auch unabhängig für das Anhalten beim Start konfiguriert werden und warten, bis ein Diagnosetool einen Fortsetzungsbefehl ausgibt. Ports, die für die Verbindung konfiguriert sind, wiederholen die Verbindungsversuche auf unbestimmte Zeit, wenn der Remoteendpunkt nicht überwacht oder die Verbindung verloren geht. Die App hält verwalteten Code jedoch nicht automatisch an, während darauf gewartet wird, diese Verbindung herzustellen. Wenn die App warten soll, bis eine Verbindung hergestellt wurde, verwenden Sie die Option für das Anhalten beim Start.

Benutzerdefinierte Ports werden mithilfe der Umgebungsvariablen DOTNET_DiagnosticPorts konfiguriert. Diese Variable muss auf eine durch Semikolons getrennte Liste von Portbeschreibungen festgelegt werden. Jede Portbeschreibung besteht aus einer Endpunktadresse und optionalen Modifizierern, welche die connect- oder listen-Rolle der Runtime steuern und bestimmen, ob die Runtime beim Start angehalten werden soll. Unter Windows ist die Endpunktadresse der Name einer Named Pipe ohne das Präfix \\.\pipe\. Unter Linux und macOS ist sie der vollständige Pfad zu einem Unix Domain Socket. Unter Android, iOS und tvOS ist die Adresse eine Kombination aus IP-Adresse und Port. Beispiel:

  1. DOTNET_DiagnosticPorts=my_diag_port1 (Windows): Die Runtime stellt eine Verbindung mit der Named Pipe \\.\pipe\my_diag_port1 her.
  2. DOTNET_DiagnosticPorts=/foo/tool1.socket;foo/tool2.socket (Linux und macOS): Die Runtime stellt eine Verbindung mit den Unix Domain Sockets /foo/tool1.socket und /foo/tool2.socket her.
  3. DOTNET_DiagnosticPorts=127.0.0.1:9000 (Android, iOS und tvOS): Die Runtime stellt eine Verbindung mit der IP-Adresse 127.0.0.1 am Port 9000 her.
  4. DOTNET_DiagnosticPorts=/foo/tool1.socket,nosuspend (Linux und macOS): Dieses Beispiel enthält den Modifizierer nosuspend. Die Laufzeit versucht, eine Verbindung mit Unix Domain Socket /foo/tool1.socket herzustellen, die ein externes Tool erstellt. Zusätzliche Diagnoseports führen normalerweise dazu, dass die Runtime beim Start angehalten wird und auf einen Fortsetzungsbefehl wartet. nosuspend sorgt aber dafür, dass die Runtime nicht wartet.

Die vollständige Syntax für einen Port ist address[,(listen|connect)][,(suspend|nosuspend)]. connect ist die Standardeinstellung, wenn weder connect noch listen angegeben sind (und listen wird nur von der Mono-Laufzeit unter Android oder iOS unterstützt). suspend ist die Standardeinstellung, wenn weder suspend noch nosuspend angegeben ist.

Verwendung in dotnet-Diagnosetools

Tools wie dotnet-dump, dotnet-counters und dotnet-trace unterstützen jeweils collect- oder monitor-Verben, die über den Diagnoseport mit einer .NET-App kommunizieren.

  • Wenn diese Tools das Argument --processId verwenden, berechnet das Tool automatisch die Standardadresse des Diagnoseports und stellt eine Verbindung damit her.
  • Beim Angeben des Arguments --diagnostic-port lauscht das Tool an der angegebenen Adresse, und Sie sollten die Umgebungsvariable DOTNET_DiagnosticPorts verwenden, um Ihre App für die Verbindungsherstellung zu konfigurieren. Ein vollständiges Beispiel mit „dotnet-counters“ finden Sie unter Verwenden des Diagnoseports.

Verwenden eines Proxys für den Diagnoseport mithilfe von „ds-router“

Von allen Diagnosetools vom Typ „dotnet-*“ wird erwartet, dass sie eine Verbindung mit einem Diagnoseport herstellen, bei dem es sich um einen lokalen Named Pipe- oder Unix-Domänensocket handelt. Mono wird häufig auf isolierter Hardware oder in Emulatoren ausgeführt, die einen Proxy über TCP benötigen, um den Zugriff darauf zu ermöglichen. Das Tool dotnet-dsrouter kann eine lokale Named Pipe oder einen Unix Domain Socket per Proxy an TCP weiterleiten, damit die Tools in diesen Umgebungen verwendet werden können. Weitere Informationen finden Sie unter dotnet-dsrouter.