Portas de diagnóstico

Este artigo se aplica a: ✔️ .NET Core 3.1 e versões posteriores

O runtime do .NET Core expõe um ponto de extremidade de serviço que permite que outros processos enviem comandos de diagnóstico e recebam respostas em um canal de IPC. Esse ponto de extremidade é chamado de porta de diagnóstico. Os comandos podem ser enviados à porta de diagnóstico para:

  • Capturar um despejo de memória.
  • Iniciar um rastreamento de EventPipe.
  • Solicitar a linha de comando usada para iniciar o aplicativo.

A porta de diagnóstico dá suporte a transportes diferentes dependendo da plataforma. No momento, as implementações de runtime CoreCLR e Mono usam Pipes Nomeados no Windows e Soquetes de Domínio do Unix no Linux e no macOS. A implementação de runtime Mono no Android, no iOS e no tvOS usa TCP/IP. O canal usa um protocolo binário personalizado. A maioria dos desenvolvedores nunca interagirá diretamente com o canal e o protocolo subjacentes, mas usará ferramentas de GUI ou CLI que se comunicam em seu nome. Por exemplo, as ferramentas dotnet-dump e dotnet-trace abstraem o envio de comandos de protocolo para capturar despejos e iniciar rastreamentos. Para desenvolvedores que desejam escrever ferramentas personalizadas, o pacote NuGet Microsoft.Diagnostics.NETCore.Client fornece uma abstração da API do .NET do transporte e do protocolo subjacentes.

Considerações de segurança

A porta de diagnóstico expõe informações confidenciais sobre um aplicativo em execução. Se um usuário não confiável obtiver acesso a esse canal, ele poderá observar o estado detalhado do programa, incluindo todos os segredos que estão na memória e modificar arbitrariamente a execução do programa. No runtime CoreCLR, a porta de diagnóstico padrão é configurada para ser acessível apenas pela mesma conta de usuário que iniciou o aplicativo ou por uma conta com permissões de superusuário. Se o modelo de segurança não confiar em outros processos com as mesmas credenciais de conta de usuário, você poderá desabilitar todas as portas de diagnóstico por meio da definição da variável de ambiente DOTNET_EnableDiagnostics=0. Essa configuração bloqueará sua capacidade de usar ferramentas externas, como a depuração do .NET ou as ferramentas de diagnóstico do dotnet-*.

Observação

O .NET 6 usa o prefixo DOTNET_ como padrão em vez de COMPlus_ para variáveis de ambiente que configuram o comportamento de tempo de execução do .NET. No entanto, o prefixo COMPlus_ continuará funcionando. Se você estiver usando uma versão anterior do runtime do .NET, continue usando o prefixo COMPlus_ para variáveis de ambiente.

Porta de diagnóstico padrão

No Windows, no Linux e no macOS, o runtime tem uma porta de diagnóstico aberta por padrão em um ponto de extremidade conhecido. Essa é a porta com a qual as ferramentas de diagnóstico do dotnet-* se conectam automaticamente quando não foram configuradas explicitamente para usar uma porta alternativa. O ponto de extremidade é:

  • Windows – Pipe nomeado \\.\pipe\dotnet-diagnostic-{pid}
  • Linux e macOS – Soquete de Domínio do Unix {temp}/dotnet-diagnostic-{pid}-{disambiguation_key}-socket

{pid} é a ID do processo escrita em decimal, {temp} é a variável de ambiente TMPDIR ou o valor /tmp quando TMPDIR é indefinido/vazio e {disambiguation_key} é a hora de início do processo gravada em decimal. No macOS e no NetBSD, a hora de início do processo é o número de segundos desde a época do UNIX. Em todas as outras plataformas, são jiffies desde o tempo de inicialização.

Suspender o runtime na inicialização

Por padrão, o runtime executa o código gerenciado assim que é iniciado, independentemente de uma ferramenta de diagnóstico ter se conectado à porta de diagnóstico. Às vezes, é útil que o runtime espere para executar o código gerenciado até que uma ferramenta de diagnóstico seja conectada, para observar o comportamento inicial do programa. A configuração da variável de ambiente DOTNET_DefaultDiagnosticPortSuspend=1 faz com que o runtime aguarde até que uma ferramenta se conecte à porta padrão. Se nenhuma ferramenta for anexada após vários segundos, o runtime imprimirá uma mensagem de aviso no console explicando que ainda está aguardando a anexação de uma ferramenta.

Configurar portas de diagnóstico adicionais

Observação

Isso funciona apenas para aplicativos que executam o .NET 5 ou posterior.

Os runtime Mono e CoreCLR podem usar portas de diagnóstico configuradas personalizadas na função connect. O Mono também dá suporte a portas TCP/IP personalizadas na função listen, quando usado com dotnet-dsrouter no Android ou iOS. Essas portas personalizadas são adicionais à porta padrão que permanece disponível. Há alguns motivos comuns para que as portas personalizadas sejam úteis:

  • No Android, iOS e tvOS não há porta padrão, portanto, a configuração de uma porta é necessária para usar ferramentas de diagnóstico.
  • Em ambientes com contêineres ou firewalls, convém configurar um endereço do ponto de extremidade previsível que não varie de acordo com a ID do processo, como a porta padrão faz. Depois, a porta personalizada pode ser adicionada explicitamente a uma lista de permissões ou receber um proxy em algum limite de segurança.
  • Para ferramentas de monitoramento, é bom fazer com que a ferramenta ouça em um ponto de extremidade para que o runtime tente se conectar a ela ativamente. Isso evita a necessidade da ferramenta de monitoramento sondar continuamente a inicialização de novos aplicativos. Em ambientes em que a porta de diagnóstico padrão não está acessível, isso também evita a necessidade de configurar o monitor com um ponto de extremidade personalizado para cada aplicativo monitorado.

Em cada canal de comunicação entre uma ferramenta de diagnóstico e o runtime do .NET, um lado precisa ser o ouvinte e aguardar o outro lado se conectar. O runtime pode ser configurado para atual na função connect para qualquer porta. (O runtime Mono também pode ser configurado para atual na função listen para qualquer porta.) As portas também podem ser configuradas de modo independente para serem suspensas na inicialização, aguardando que uma ferramenta de diagnóstico emita um comando de retomada. As portas configuradas para se conectar repetirão suas tentativas de conexão indefinidamente se o ponto de extremidade remoto não estiver escutando ou se a conexão for perdida. Mas o aplicativo não suspende automaticamente o código gerenciado enquanto aguarda para estabelecer essa conexão. Se você quiser que o aplicativo aguarde a criação de uma conexão, use a opção suspender na inicialização.

As portas personalizadas são configuradas usando a variável de ambiente DOTNET_DiagnosticPorts. Essa variável deve ser definida como uma lista delimitada por ponto e vírgula de descrições de porta. Cada descrição de porta consiste em um endereço do ponto de extremidade e modificadores opcionais que controlam a função de connect ou listen do runtime e se o runtime deve ser suspenso na inicialização. No Windows, o endereço do ponto de extremidade é o nome de um pipe nomeado sem o prefixo \\.\pipe\. No Linux e no macOS, é o caminho completo para um soquete de domínio Unix. No Android, iOS e tvOS, o endereço é um IP e uma porta. Por exemplo:

  1. DOTNET_DiagnosticPorts=my_diag_port1 – (Windows) O runtime se conecta ao pipe nomeado \\.\pipe\my_diag_port1.
  2. DOTNET_DiagnosticPorts=/foo/tool1.socket;foo/tool2.socket – (Linux e macOS) O runtime se conecta aos soquetes de domínio do UNIX /foo/tool1.socket e /foo/tool2.socket.
  3. DOTNET_DiagnosticPorts=127.0.0.1:9000 – (Android, iOS e tvOS) O runtime se conecta ao IP 127.0.0.1 na porta 9000.
  4. DOTNET_DiagnosticPorts=/foo/tool1.socket,nosuspend – (Linux e macOS) Este exemplo tem o modificador nosuspend. O runtime tenta se conectar ao soquete de domínio Unix /foo/tool1.socket que uma ferramenta externa cria. Portas de diagnóstico adicionais normalmente fazem com que o runtime seja suspenso na inicialização aguardando um comando de retomada, mas nosuspend faz com que o runtime não aguarde.

A sintaxe completa de uma porta é address[,(listen|connect)][,(suspend|nosuspend)]. connect será o padrão se nenhum connect ou listen for especificado (e listen só terá suporte do runtime Mono no Android ou no iOS). suspend será o padrão se não for especificado suspend nem nosuspend.

Uso em ferramentas de diagnóstico do dotnet

As ferramentas como dotnet-dump, dotnet-counters e dotnet-trace dão suporte aos verbos collect ou monitor que se comunicam com um aplicativo .NET por meio da porta de diagnóstico.

  • Quando essas ferramentas usam o argumento --processId, a ferramenta calcula automaticamente o endereço da porta de diagnóstico padrão e se conecta a ele.
  • Ao especificar o argumento --diagnostic-port, a ferramenta escuta o endereço especificado e você deve usar a variável de ambiente DOTNET_DiagnosticPorts para configurar o aplicativo para conexão. Para obter um exemplo completo com o dotnet-counters, confira Como usar a porta de diagnóstico.

Usar o ds-router para definir um proxy para a porta de diagnóstico

Todas as ferramentas de diagnóstico do dotnet-* esperam se conectar a uma porta de diagnóstico que seja um pipe nomeado ou soquete de domínio Unix. O Mono geralmente é executado em hardware isolado ou em emuladores que precisam de um proxy por TCP para ficar acessível. A ferramenta dotnet-dsrouter pode definir um proxy em um Pipe Nomeado local ou em um Soquete de Domínio do UNIX para TCP para que as ferramentas possam ser usadas nesses ambientes. Para obter mais informações, confira dotnet-dsrouter.