Monitorando aplicativos .NET

.NET é um tempo de execução gerenciado . Isso significa que ele contém funções de alto nível que controlam seu programa para você (de Introdução ao Common Language Runtime (CLR), 2007 ):


O tempo de execução oferece muitas funções, portanto, é conveniente dividi-las nas seguintes categorias:

  1. As principais funções que afetam o dispositivo de outras pessoas. Estes incluem:
    1. coleta de lixo;
    2. protegendo o acesso à memória e digite a segurança do sistema;
    3. suporte de alto nível para linguagens de programação.
  2. Funções adicionais - trabalhe com base nas principais. Muitos programas úteis ficam sem eles. Essas funções incluem:
    1. Isolando aplicativos usando AppDomains
    2. proteção de aplicativos e isolamento de sandbox.
  3. Outras funções são necessárias em todos os tempos de execução, mas elas não usam as funções básicas do CLR. Tais recursos refletem o desejo de criar um ambiente de programação completo. Estes incluem:
    1. controle de versão;
    2. depuração / criação de perfil;
    3. garantindo interação.

Pode-se observar que, embora a depuração e a criação de perfil não sejam as funções principais ou adicionais, elas estão na lista devido ao " desejo de criar um ambiente de programação completo ".



O restante da publicação descreve quais recursos de monitoramento , observabilidade e introspecção existem no Core CLR, por que são úteis e como o ambiente os fornece.


Diagnóstico


Primeiro, dê uma olhada nas informações de diagnóstico fornecidas pelo CLR. Tradicionalmente, o Rastreamento de Eventos para Windows (ETW) é usado para isso.
Existem muitos eventos sobre os quais o CLR fornece informações. Eles estão associados a:


  • coleta de lixo (GC);
  • Compilação JIT;
  • módulos e domínios de aplicativo;
  • trabalhe com threads e conflitos ao bloquear;
  • assim como muitos outros.

Por exemplo, aqui um evento ocorre durante o carregamento no AppDomain , aqui o evento está associado ao lançamento de uma exceção e aqui ao ciclo de alocação de memória pelo coletor de lixo .


Visualização Perf


Se você deseja ver os eventos no sistema de rastreamento (ETW) relacionados aos seus aplicativos .NET, recomendo usar a excelente ferramenta PerfView e começar com esses vídeos de treinamento ou com esta apresentação do PerfView: The Ultimate .NET Performance Tool . O PerfView foi amplamente reconhecido por fornecer informações valiosas. Por exemplo, os engenheiros da Microsoft costumam usá-lo para analisar o desempenho .


Infraestrutura comum


Se de repente o nome não estiver claro, o rastreamento de eventos no ETW estará disponível apenas no Windows, o que não se encaixa muito bem no mundo entre plataformas do .NET Core. Você pode usar o PerfView para analisar o desempenho no Linux (usando LTTng). No entanto, essa ferramenta de linha de comando, chamada PerfCollect, coleta apenas dados. Atualmente, os recursos de análise e uma interface de usuário avançada (incluindo gráficos de flama ) estão disponíveis apenas nas soluções Windows.


Mas se você ainda deseja analisar o desempenho do .NET no Linux, há outras abordagens:



O segundo link acima leva a uma discussão sobre a nova infraestrutura EventPipe , que está sendo trabalhada no .NET Core (além de EventSources & EventListeners). Seus objetivos de desenvolvimento podem ser encontrados no documento Design de monitoramento de desempenho de plataforma cruzada . Em um nível alto, essa infraestrutura criará um único local para o qual o CLR enviará eventos relacionados ao diagnóstico e desempenho. Em seguida, esses eventos serão redirecionados para um ou mais registradores, que, por exemplo, podem incluir ETW, LTTng e BPF. O criador de logs necessário será determinado dependendo do sistema operacional ou plataforma em que o CLR está sendo executado. Para obter uma explicação detalhada dos prós e contras de várias tecnologias de log, consulte o .NET Cross-Plat Performance and Eventing Design .


O progresso dos EventPipes é monitorado através do projeto de Monitoramento de Desempenho e dos problemas relacionados ao 'EventPipe' .


Planos futuros


Por fim, há planos para criar um Performance Profiling Controller , que possui as seguintes tarefas:


O controlador deve gerenciar a infraestrutura de criação de perfil e apresentar os dados de desempenho gerados pelos componentes .NET responsáveis ​​pelo diagnóstico de desempenho de maneira simples e em várias plataformas.


De acordo com o plano, o controlador deve fornecer a seguinte funcionalidade por meio do servidor HTTP , recebendo todos os dados necessários da infraestrutura do EventPipes:


APIs REST


  • Princípio 1: criação de perfil simples: perfile o tempo de execução ao longo do período X e retorne o rastreio.
  • Princípio 1: criação de perfil avançado: inicie o rastreamento (junto com a configuração)
  • Princípio 1: criação de perfil avançado: rastreamento completo (a resposta a esta chamada será o próprio rastreamento).
  • Princípio 2: obtenha estatísticas associadas a todos os EventCounters ou a um EventCounter específico.

Páginas navegáveis ​​em HTML


  • Princípio 1: Uma representação textual de todas as pilhas de código gerenciado em um processo.
    • Cria capturas instantâneas de processos em execução para serem usadas como um relatório de diagnóstico simples.
  • Princípio 2: exibindo o estado atual (possivelmente com um histórico) dos contadores EventCounters.
    • Fornece uma visão geral dos contadores existentes e seus valores.
    • PROBLEMA NÃO RESOLVIDO: acho que não há APIs públicas necessárias para contar os EventCounters.

Eu realmente quero ver o que acontece com o controlador de perfil de desempenho (PPC?). Eu acho que se for incorporado ao CLR, trará muitos benefícios para o .NET. Essa funcionalidade existe em outros tempos de execução .


Criação de perfil


Outra ferramenta eficaz que o CLR possui é uma API de criação de perfil. É (principalmente) usado por ferramentas de terceiros para conectar-se ao tempo de execução em um nível baixo. Você pode aprender mais sobre a API nesta revisão , mas em um nível alto, pode usá-la para fazer retornos de chamada que são ativados se:


  • eventos relacionados ao coletor de lixo;
  • exceções são lançadas;
  • montagens são carregadas / descarregadas;
  • e muito mais

Imagem da página da API de criação de perfil do BOTR - Visão geral


Além disso, possui outros recursos eficazes. Primeiro, você pode configurar manipuladores que são chamados toda vez que o método .NET é executado, seja no próprio ambiente ou no código do usuário. Esses retornos de chamada são conhecidos como manipuladores Enter / Leave. Aqui está um bom exemplo de como usá-los. No entanto, para isso, é necessário entender as convenções de chamada para diferentes arquiteturas de SO e CPU , o que nem sempre é fácil . Além disso, lembre-se de que a API de criação de perfil é um componente COM que só pode ser acessado pelo código C / C ++, mas não pelo C # / F # / VB.NET.


Em segundo lugar, o criador de perfil pode reescrever o código IL de qualquer método .NET antes da compilação JIT usando a API SetILFunctionBody () . Essa API é realmente eficiente. Está subjacente a muitas ferramentas .NET do APM . Você pode aprender mais sobre o seu uso no meu post Como zombar de classes seladas e métodos estáticos e códigos relacionados.


API ICorProfiler


Acontece que a API de criação de perfil funcionou, deve haver todos os tipos de truques no ambiente de tempo de execução. Veja a discussão na página Permitir rejeição no anexo (consulte ReJIT: um guia de instruções para obter mais informações sobre ReJIT).


Uma definição completa de todas as interfaces e retornos de chamada da API de criação de perfil pode ser encontrada em \ vm \ inc \ corprof.idl (consulte Linguagem de descrição da interface ). É dividido em 2 partes lógicas. Uma parte é a interface Profiler -> Runtime Environment (EE) , conhecida como ICorProfilerInfo :


 //  ,    ICorProfilerInfo*,  //     .  ,  DLL   //          ,     //    . 

Isso é implementado nos seguintes arquivos:



A outra parte principal são os retornos de chamada Runtime -> Profiler, que são agrupados na interface ICorProfilerCallback :


 //       //  ICorProfilerCallaback* .       // ,     EEToProfInterfaceImpl. 

Esses retornos de chamada são implementados nos seguintes arquivos:



Por fim, vale ressaltar que as APIs de criação de perfil podem não funcionar em todos os SO e arquiteturas executando o .NET Core. Aqui está um exemplo: problemas de stub de chamada ELT no Linux . Consulte o Status das APIs do CoreCLR Profiler para obter mais informações.


Criação de perfil v. Depuração


Como uma pequena digressão, devo dizer que a criação de perfil e a depuração ainda se sobrepõem um pouco. Portanto, é útil entender o que as diferentes APIs fornecem no contexto do .NET Runtime (extraído de CLR Debugging vs. CLR Profiling ).


A diferença entre depuração e criação de perfil no CLR


DepuraçãoCriação de perfil
Projetado para encontrar problemas com a correção do código.Projetado para diagnosticar e solucionar problemas de desempenho.
Pode ter um nível muito alto de interferência.Geralmente, tem um baixo nível de intervenção. Embora o criador de perfil suporte a modificação do código IL ou a instalação de manipuladores enter / leave, tudo isso é para instrumentação, não para alterações radicais no código.
A tarefa principal é o controle completo do alvo. Isso inclui inspeção, controle de execução (por exemplo, o comando set-next-statement) e modificações (função Edit-and-Continue).A principal tarefa é inspecionar o alvo. Para isso, é fornecida instrumentação (alteração do código IL, instalação de manipuladores de entrada / saída)
API extensa e um modelo de objeto espesso preenchido com abstrações.Uma pequena API. Existem poucas ou nenhuma abstração.
Alto nível de interatividade: as ações do depurador são controladas pelo usuário (ou algoritmo). De fato, editores e depuradores são frequentemente integrados (IDEs).Sem interatividade: os dados geralmente são coletados sem a intervenção do usuário e depois analisados.
Um pequeno número de alterações críticas se a compatibilidade com versões anteriores for necessária. Pensamos que migrar da versão 1.1 para a versão 2.0 do criador de perfil será uma tarefa simples ou não muito difícil.Um grande número de alterações críticas se a compatibilidade com versões anteriores for necessária. Acreditamos que migrar da versão 1.1 para a versão 2.0 do criador de perfil será uma tarefa difícil, idêntica à reescrevê-la completamente.

Depuração


Os desenvolvedores entendem de maneira diferente o que é depuração. Por exemplo, perguntei no Twitter "como você depura programas .NET" e obtive muitas respostas diferentes . Ao mesmo tempo, as respostas continham uma boa lista de ferramentas e métodos, por isso recomendo analisá-las. Obrigado #LazyWeb


Eu acho que o melhor de toda a essência da depuração reflete esta mensagem:



O CLR fornece uma extensa lista de recursos relacionados à depuração. No entanto, por que esses fundos são necessários? Pelo menos três razões são mencionadas nesta ótima postagem. Por que a depuração gerenciada é diferente da depuração nativa? :


  1. A depuração de código não gerenciado pode ser abstraída no nível do hardware, mas o código gerenciado de depuração deve ser abstraído no nível do código IL.
  2. A depuração do código gerenciado requer muitas informações que não estão disponíveis antes da execução.
  3. O depurador de código gerenciado deve coordenar-se com o coletor de lixo (GC)

Portanto, para facilitar o uso, o CLR deve fornecer uma API de depuração de alto nível conhecida como ICorDebug . É mostrado na figura abaixo, mostrando um cenário geral de depuração (fonte: BOTR):


API ICorDebug


O princípio de implementação e a descrição dos vários componentes são extraídos da CLR Debugging, uma breve introdução :


Todo o suporte à depuração no .Net é implementado na parte superior da biblioteca DLL, que chamamos de DAC. Esse arquivo (geralmente chamado mscordacwks.dll ) é um elemento estrutural da API de depuração pública ( ICorDebug ) e de duas APIs de depuração privadas: SOS-Dac API e IXCLR.
Em um mundo ideal, todos usariam o ICorDebug , nossa API pública. No entanto, o ICorDebug não possui muitos dos recursos que os desenvolvedores de ferramentas precisam. Este é o problema que estamos tentando corrigir onde pudermos. No entanto, esses aprimoramentos estão presentes apenas no CL.v.next, mas não nas versões anteriores do CLR. De fato, o suporte à depuração de despejo de memória apareceu na API ICorDebug apenas com o lançamento do CLR v4. Todo mundo que usa despejos de memória para depuração no CLR v2 não poderá aplicar o ICorDebug .

(Consulte SOS e ICorDebug para obter mais informações)


De fato, a API ICorDebug é dividida em mais de 70 interfaces. Não darei a todos, mas mostrarei por quais categorias eles podem ser divididos. Para mais informações, consulte a Partição do ICorDebug, onde esta lista foi publicada.


  • Nível superior : ICorDebug + ICorDebug2 - interfaces de nível superior que servem perfeitamente como uma coleção de objetos ICorDebugProcess.
  • Retornos de chamada: os eventos de depuração de código gerenciado são enviados por métodos ao objeto de retorno de chamada implementado pelo depurador.
  • Processo : esse conjunto de interfaces representa o código de trabalho e inclui APIs relacionadas a eventos.
  • Inspeção de código / tipo : funciona principalmente com imagens estáticas de PE, mas existem métodos convenientes para dados reais.
  • Controle de execução : capacidade de monitorar o progresso do encadeamento. Na prática, isso significa a capacidade de definir pontos de interrupção (F9) e percorrer o código (entrada do código F11, desvio do código F10, saída do código S + F11). A função de controle de execução ICorDebug funciona apenas no código gerenciado.
  • Threads + pilhas de chamadas : as pilhas de chamadas são a base para as funções de inspeção implementadas pelo depurador. O trabalho com a pilha de chamadas é realizado usando as seguintes interfaces. O ICorDebug suporta apenas a depuração de código gerenciado e, portanto, você pode rastrear a pilha de apenas código gerenciado.
  • Inspeção de objetos : a inspeção de objetos faz parte da API que permite ver os valores das variáveis ​​no código depurado. Para cada interface, dou o método MVP, que, ao que me parece, deve descrever brevemente o objetivo dessa interface.

Como nas APIs de criação de perfil, os níveis de suporte à API de depuração variam de acordo com a arquitetura do SO e do processador. Por exemplo, em agosto de 2018, ainda não havia solução Linux ARM para diagnosticar e depurar código gerenciado. Para obter mais informações sobre o suporte ao Linux, consulte a publicação Debugging .NET Core no Linux com LLDB e o repositório Diagnostics da Microsoft, que visa facilitar a depuração de programas .NET para Linux.


Por fim, se você quiser ver como a API do ICorDebug aparece em C #, dê uma olhada nos wrappers da biblioteca CLRMD, incluindo todos os retornos de chamada disponíveis (mais sobre CLRMD será discutido mais adiante nesta postagem).


SOS e DAC


O Data Access Component (DAC) é discutido em detalhes na página BOTR . Essencialmente, fornece acesso fora de processo às estruturas de dados do CLR, para que as informações contidas nelas possam ser lidas em outro processo. Assim, o depurador (via ICorDebug ) ou a extensão 'Son of Strike' (SOS) pode acessar a instância CLR ou despejo de memória em execução e encontrar, por exemplo:


  • todos os threads em execução;
  • objetos de heap gerenciado
  • informações completas sobre o método, incluindo código de máquina;
  • rastreamento de pilha atual.

Uma pequena digressão : se você quiser descobrir de onde esses nomes estranhos vieram e obter uma pequena lição na história do .NET, confira esta resposta no Stack Overflow .


A lista completa de comandos do SOS é impressionante . Se você usá-lo junto com o WinDBG, poderá descobrir o que está acontecendo dentro do seu programa e do CLR em um nível muito baixo. Para ver como tudo é implementado, vejamos o !HeapStat , que exibe uma descrição dos tamanhos dos vários heaps que o .NET GC usa:


(Imagem retirada do SOS: A próxima versão possui alguns novos comandos - HeapStat)


Aqui está um fluxo de código que mostra como o SOS e o DAC funcionam juntos:


  • Equipe completa do SOS !HeapStat ( link )
  • Código SOS no !HeapStat que funciona com o GC da Estação de Trabalho (link)
  • Função SOS GCHeapUsageStats(..) , que executa a parte mais difícil do trabalho ( link )
  • DacpGcHeapDetails dados compartilhada DacpGcHeapDetails que contém ponteiros para os dados principais no heap do GC, como segmentos, máscaras de bits e gerações individuais ( referência ).
  • GetGCHeapStaticData DAC GetGCHeapStaticData que preenche a estrutura DacpGcHeapDetails ( link )
  • DacpHeapSegmentData dados compartilhada DacpHeapSegmentData que contém informações sobre um segmento de heap de GC individual ( link )
  • DAC GetHeapSegmentData(..) , que preenche a estrutura DacpHeapSegmentData ( link )

Depuradores de terceiros


Desde que a Microsoft publicou a API de depuração, os desenvolvedores de terceiros puderam usar as interfaces ICorDebug . Aqui está uma lista dos que eu consegui encontrar:



Despejos de memória


A última coisa sobre a qual falaremos é sobre despejos de memória, que podem ser obtidos em um sistema em funcionamento e analisados ​​fora dele. O tempo de execução do .NET sempre oferece suporte à descarga de memória no Windows . E agora que o .NET Core se tornou multiplataforma, surgiram ferramentas que executam a mesma tarefa em outros sistemas operacionais.


Ao usar despejos de memória, às vezes é difícil obter as versões corretas e correspondentes dos arquivos SOS e DAC. Felizmente, a Microsoft lançou recentemente a ferramenta CLI do dotnet symbol , que:


pode baixar todos os arquivos necessários para depuração (conjuntos de caracteres, módulos, arquivos SOS e DAC para um módulo coreclr específico) para qualquer dump, minidump ou arquivos específicos de qualquer plataforma suportada, incluindo ELF, MachO, DLL do Windows, PDB e portátil APO

Por fim, se você estiver analisando um pouco os despejos de memória, recomendo dar uma olhada na excelente biblioteca de CLR MD lançada há vários anos pela Microsoft. Eu já escrevi sobre suas funções. Em resumo, usando a biblioteca, você pode trabalhar com despejos de memória por meio de uma API C # intuitiva que contém classes que fornecem acesso ao ClrHeap, raízes de GC, threads de CLR, quadros de pilha e muito mais. De fato, o CLR MD pode implementar a maioria (se não todos) dos comandos do SOS.


Você pode descobrir como funciona a partir desta postagem :


A Biblioteca gerenciada do ClrMD é um invólucro em torno das APIs de depuração destinadas ao uso interno apenas no CLR. Apesar de essas APIs serem muito eficazes para o diagnóstico, não as suportamos na forma de lançamentos documentados públicos, pois seu uso é complexo e está intimamente relacionado a outros recursos da implementação do CLR. O ClrMD resolve esse problema fornecendo um wrapper gerenciável e fácil de usar em torno dessas APIs de depuração de baixo nível.

Source: https://habr.com/ru/post/pt466097/


All Articles