Porte di diagnostica

Questo articolo si applica a: ✔️ .NET Core 3.1 e versioni successive

Il runtime .NET espone un endpoint di servizio che consente ad altri processi di inviare comandi di diagnostica e ricevere risposte tramite un canale IPC. Questo endpoint è denominato porta di diagnostica. I comandi possono essere inviati alla porta di diagnostica per:

  • Acquisire un dump della memoria.
  • Avviare una traccia EventPipe.
  • Richiedere la riga di comando usata per avviare l'app.

La porta di diagnostica supporta trasporti diversi a seconda della piattaforma. Attualmente entrambe le implementazioni di runtime CoreCLR e Mono usano named pipe in Windows e socket di dominio Unix in Linux e macOS. L'implementazione di runtime Mono in Android, iOS e tvOS usa TCP/IP. Il canale usa un protocollo binario personalizzato. La maggior parte degli sviluppatori non interagirà mai direttamente con il canale e il protocollo sottostanti, ma userà gli strumenti dell'interfaccia utente grafica o dell'interfaccia della riga di comando che comunicano per loro conto. Ad esempio, gli strumenti dotnet-dump e dotnet-trace offrono un'astrazione per l'invio dei comandi del protocollo per acquisire dump e avviare tracce. Per gli sviluppatori che vogliono scrivere strumenti personalizzati, il pacchetto NuGet Microsoft.Diagnostics.NETCore.Client fornisce un'astrazione dell'API .NET del trasporto e del protocollo sottostanti.

Considerazioni sulla sicurezza

La porta di diagnostica espone informazioni sensibili su un'applicazione in esecuzione. Se un utente non attendibile ottiene l'accesso a questo canale, può osservare lo stato dettagliato del programma, inclusi eventuali segreti in memoria, e modificare arbitrariamente l'esecuzione del programma. Nel runtime CoreCLR la porta di diagnostica predefinita è configurata per essere accessibile solo dallo stesso account utente che ha avviato l'app o da un account con autorizzazioni di utente con privilegi avanzati. Se il modello di sicurezza non considera attendibili altri processi con le stesse credenziali dell'account utente, è possibile disabilitare tutte le porte di diagnostica impostando la variabile di ambiente DOTNET_EnableDiagnostics=0. Questa impostazione bloccherà la possibilità di usare strumenti esterni, ad esempio il debug .NET o uno degli strumenti di diagnostica dotnet-*.

Nota

.NET 6 standardizza il prefisso DOTNET_ anziché quello di COMPlus_ per le variabili di ambiente che configurano il comportamento in fase di esecuzione di .NET. Tuttavia, il prefisso diCOMPlus_ continuerà a funzionare. Se si usa una versione precedente del runtime .NET, è comunque consigliabile usare il prefisso COMPlus_ per le variabili di ambiente.

Porta di diagnostica predefinita

In Windows, Linux e macOS, il runtime ha una porta di diagnostica aperta per impostazione predefinita in un endpoint noto. Questa è la porta a cui si connettono automaticamente gli strumenti di diagnostica dotnet-* quando non sono stati configurati in modo esplicito per l'uso di una porta alternativa. L'endpoint è:

  • Windows - Named pipe \\.\pipe\dotnet-diagnostic-{pid}
  • Linux e macOS - socket di dominio Unix {temp}/dotnet-diagnostic-{pid}-{disambiguation_key}-socket

{pid} è l'ID del processo scritto in formato decimale, {temp} è la variabile di ambiente TMPDIR o il valore /tmp se TMPDIR è indefinito/vuoto e {disambiguation_key} è l'ora di inizio del processo scritta in formato decimale. In macOS e NetBSD, l'ora di inizio del processo è il numero di secondi trascorsi dall'epoca di UNIX. In tutte le altre piattaforme si tratta di un attimo dall'ora di avvio.

Sospendere il runtime all'avvio

Per impostazione predefinita, il runtime esegue il codice gestito non appena viene avviato, indipendentemente dal fatto che gli strumenti di diagnostica siano connessi alla porta di diagnostica. A volte è utile fare in modo che il runtime attenda l'esecuzione del codice gestito fino a quando non viene connesso uno strumento di diagnostica, per osservare il comportamento iniziale del programma. L'impostazione della variabile di ambiente DOTNET_DefaultDiagnosticPortSuspend=1 determina l'attesa del runtime fino a quando uno strumento non si connette alla porta predefinita. Se non viene collegato alcun strumento dopo alcuni secondi, il runtime visualizza un messaggio di avviso nella console che spiega che è ancora in attesa del collegamento di uno strumento.

Configurare porte di diagnostica aggiuntive

Nota

Questa operazione funziona solo per le app che eseguono .NET 5 o versioni successive.

Sia i runtime Mono che CoreCLR possono usare porte di diagnostica configurate personalizzate nel ruolo connect. Mono supporta anche porte TCP/IP personalizzate nel ruolo listen, se usate con dotnet-dsrouter in Android o iOS. Queste porte personalizzate si aggiungono alla porta predefinita che rimane disponibile. Esistono alcuni motivi comuni per cui le porte personalizzate sono utili:

  • In Android, iOS e tvOS non è presente alcuna porta predefinita, quindi la configurazione di una porta è necessaria per usare gli strumenti di diagnostica.
  • Negli ambienti con contenitori o firewall, può essere necessario configurare un indirizzo endpoint prevedibile che non varia in base all'ID del processo, come la porta predefinita. È quindi possibile aggiungere la porta personalizzata in modo esplicito a un elenco di elementi consentiti o inoltrata tramite proxy attraverso alcuni limiti di sicurezza.
  • Per gli strumenti di monitoraggio è utile avere lo strumento in ascolto su un endpoint e il runtime tenta attivamente di connettersi. In questo modo si evita che lo strumento di monitoraggio debba eseguire il polling continuo per individuare le nuove app avviate. Negli ambienti in cui la porta di diagnostica predefinita non è accessibile, evita anche di dover configurare il monitoraggio con un endpoint personalizzato per ogni app monitorata.

In ogni canale di comunicazione tra uno strumento di diagnostica e il runtime .NET, un lato deve essere il listener e attendere che l'altro lato si connetta. Il runtime può essere configurato per agire nel ruolo connect per qualsiasi porta. Il runtime Mono può anche essere configurato per agire nel ruolo listen per qualsiasi porta. Le porte possono anche essere configurate in modo indipendente per la sospensione all'avvio, in attesa che uno strumento di diagnostica invii un comando di ripresa. Le porte configurate per la connessione ripetono i tentativi di connessione a tempo indeterminato se l'endpoint remoto non è in ascolto o se la connessione viene persa. Tuttavia, l'app non sospende automaticamente il codice gestito durante l'attesa di stabilire tale connessione. Se si vuole che l'app attenda che venga stabilita una connessione, usare l'opzione per la sospensione all'avvio.

Le porte personalizzate vengono configurate usando la variabile di ambiente DOTNET_DiagnosticPorts. Questa variabile deve essere impostata su un elenco delimitato da punto e virgola delle descrizioni delle porte. Ogni descrizione della porta è costituita da un indirizzo endpoint e da modificatori facoltativi che controllano il ruolo connect o listen del runtime e se il runtime deve essere sospeso all'avvio. In Windows, l'indirizzo dell'endpoint è il nome di una named pipe senza il prefisso \\.\pipe\. In Linux e macOS è il percorso completo di un socket di dominio Unix. In Android, iOS e tvOS l'indirizzo è composto da un IP e una porta. Ad esempio:

  1. DOTNET_DiagnosticPorts=my_diag_port1 - (Windows) Il runtime si connette alla named pipe \\.\pipe\my_diag_port1.
  2. DOTNET_DiagnosticPorts=/foo/tool1.socket;foo/tool2.socket - (Linux e macOS) Il runtime si connette ai socket di dominio Unix /foo/tool1.socket e /foo/tool2.socket.
  3. DOTNET_DiagnosticPorts=127.0.0.1:9000 - (Android, iOS e tvOS) Il runtime si connette all'indirizzo IP 127.0.0.1 sulla porta 9000.
  4. DOTNET_DiagnosticPorts=/foo/tool1.socket,nosuspend - (Linux e macOS) Questo esempio ha il modificatore nosuspend. Il runtime tenta di connettersi al socket di dominio Unix /foo/tool1.socket creato da uno strumento esterno. Le porte di diagnostica aggiuntive normalmente causerebbero la sospensione del runtime all'avvio in attesa di un comando di ripresa, ma nosuspend fa sì che il runtime non attenda.

La sintassi completa per una porta è address[,(listen|connect)][,(suspend|nosuspend)]. connect è l'impostazione predefinita se non si specifica connect o listen (e listen è supportato solo dal runtime Mono in Android o iOS). suspend è l'impostazione predefinita se non si specifica suspend o nosuspend.

Utilizzo negli strumenti di diagnostica dotnet

Strumenti come dotnet-dump, dotnet-counters e dotnet-trace supportano tutti verbi collect o monitor che comunicano con un'app .NET tramite la porta di diagnostica.

  • Quando questi strumenti usano l'argomento --processId, lo strumento calcola automaticamente l'indirizzo della porta di diagnostica predefinita e si connette.
  • Quando si specifica l'argomento --diagnostic-port, lo strumento è in ascolto all'indirizzo specificato ed è necessario usare la variabile di ambiente DOTNET_DiagnosticPorts per configurare l'app per la connessione. Per un esempio completo con dotnet-counters, vedere Uso della porta di diagnostica.

Usare ds-router per inoltrare tramite proxy la porta di diagnostica

Tutti gli strumenti di diagnostica dotnet-* prevedono di connettersi a una porta di diagnostica che è una named pipe o un socket di dominio Unix locale. Mono viene spesso eseguito su hardware isolato o in emulatori che necessitano di un proxy su TCP per diventare accessibili. Lo strumento dotnet-dsrouter può inoltrare tramite proxy una named pipe o un socket di dominio Unix locale a TCP in modo che gli strumenti possano essere usati in tali ambienti. Per altre informazioni, vedere dotnet-dsrouter.