Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Um perfilador é uma ferramenta que monitoriza a execução de outra aplicação. Um perfilador de execução de linguagem comum (CLR) é uma biblioteca de ligação dinâmica (DLL) que consiste em funções que recebem mensagens e enviam mensagens para o CLR através da API de perfilamento. A DLL do perfilador é carregada pelo CLR em tempo de execução.
As ferramentas tradicionais de perfilagem focam-se na medição da execução da aplicação. Ou seja, medem o tempo gasto em cada função ou o uso de memória da aplicação ao longo do tempo. A API de perfilação direciona-se para uma classe mais ampla de ferramentas de diagnóstico, como utilitários de cobertura de código e até auxílios avançados de depuração. Estas utilizações são todas de natureza diagnóstica. A API de perfilagem não só mede, como também monitoriza a execução de uma aplicação. Por esta razão, a API de perfilagem nunca deve ser usada pela própria aplicação, e a execução da aplicação não deve depender (nem ser afetada por) o profiler.
Perfilar uma aplicação CLR requer mais suporte do que perfilar código máquina compilado convencionalmente. Isto deve-se ao facto de o CLR introduzir conceitos como domínios de aplicação, recolha de lixo, gestão gerida de exceções, compilação just-in-time (JIT) de código (convertendo código common intermediate language, ou CIL, em código de máquina nativo) e funcionalidades semelhantes. Os mecanismos convencionais de perfilagem não conseguem identificar nem fornecer informações úteis sobre estas características. A API de perfilagem fornece esta informação em falta de forma eficiente, com impacto mínimo no desempenho do CLR e da aplicação perfilada.
A compilação do JIT em tempo de execução oferece boas oportunidades para perfilamento. A API de perfilagem permite que um profiler altere o fluxo de código CIL em memória para uma rotina antes de esta ser compilada por JIT. Desta forma, o profiler pode adicionar dinamicamente código de instrumentação a rotinas específicas que necessitam de uma investigação mais aprofundada. Embora esta abordagem seja possível em cenários convencionais, é muito mais fácil de implementar para o CLR usando a API de perfilação.
A API de Perfilação
Normalmente, a API de perfilagem é usada para escrever um profile de código, que é um programa que monitoriza a execução de uma aplicação gerida.
A API de perfilagem é usada por uma DLL de perfilador, que é carregada no mesmo processo da aplicação que está a ser perfilada. A DLL do profiler implementa uma interface de callback (ICorProfilerCallback no .NET Framework versões 1.0 e 1.1, ICorProfilerCallback2 na versão 2.0 e posteriores). O CLR chama os métodos nessa interface para notificar o profiler sobre eventos no processo perfilado. O profiler pode voltar a ligar para o runtime utilizando os métodos nas interfaces ICorProfilerInfo e ICorProfilerInfo2 para obter informações sobre o estado da aplicação perfilada.
Observação
Apenas a parte de recolha de dados da solução do perfilador deve estar a correr no mesmo processo que a aplicação perfilada. Toda a interface de utilizador e análise de dados deve ser realizada num processo separado.
A ilustração seguinte mostra como a DLL do perfilador interage com a aplicação que está a ser perfilada e com o CLR.
As Interfaces de Notificação
ICorProfilerCallback e ICorProfilerCallback2 podem ser considerados interfaces de notificação. Estas interfaces consistem em métodos como ClassLoadStarted, ClassLoadFinished e JITCompilationStarted. Cada vez que o CLR carrega ou descarrega uma classe, compila uma função, e assim sucessivamente, chama o método correspondente no perfilador ICorProfilerCallback ou ICorProfilerCallback2 na interface.
Por exemplo, um perfilador poderia medir o desempenho do código através de duas funções de notificação: FunctionEnter2 e FunctionLeave2. Limita-se a marcar o horário de cada notificação, acumular resultados e gerar uma lista que indica quais as funções que consumiram mais tempo de CPU ou relógio de parede durante a execução da aplicação.
As Interfaces de Recuperação de Informação
As outras interfaces principais envolvidas no perfilamento são ICorProfilerInfo e ICorProfilerInfo2. O profiler chama estas interfaces conforme necessário para obter mais informações que ajudem a sua análise. Por exemplo, sempre que o CLR chama a função FunctionEnter2 , fornece um identificador de função. O profiler pode obter mais informações sobre essa função chamando o método ICorProfilerInfo2::GetFunctionInfo2 para descobrir a classe mãe da função, o seu nome, e assim por diante.
Funcionalidades Suportadas
A API de perfilagem fornece informações sobre uma variedade de eventos e ações que ocorrem no runtime da linguagem comum. Pode usar esta informação para monitorizar o funcionamento interno dos processos e analisar o desempenho da sua aplicação .NET Framework.
A API de perfilagem recupera informações sobre as seguintes ações e eventos que ocorrem no CLR:
Eventos de arranque e encerramento do CLR.
Eventos de criação e encerramento do domínio da aplicação.
Eventos de carregamento e descarregamento de montagens.
Eventos de carregamento e descarga de módulos.
COM vtable eventos de criação e destruição.
Eventos de compilação just-in-time (JIT) e apresentação de código.
Eventos de carregamento e descarregamento de aulas.
Eventos de criação e destruição de fios.
Eventos de entrada e saída da função.
Exceções.
Transições entre execução de código gerida e não gerida.
Transições entre diferentes contextos de execução.
Informação sobre suspensões em tempo de execução.
Informação sobre a pilha de memória em tempo de execução e a atividade de recolha de lixo.
A API de perfilagem pode ser chamada a partir de qualquer linguagem compatível com COM (não gerida).
A API é eficiente no que diz respeito ao consumo de CPU e memória. A perfilagem não envolve alterações significativas o suficiente para causar resultados enganosos na aplicação perfilada.
A API de perfilação é útil tanto para perfiladores de amostragem como para não amostragem. Um perfilador de amostragem inspeciona o perfil em tique-taques regulares do relógio, por exemplo, com intervalos de 5 milissegundos. Um perfilador não-amostragem é informado de um evento de forma síncrona com o thread que causa o evento.
Funcionalidade Não Suportada
A API de perfilagem não suporta a seguinte funcionalidade:
Código não gerido, que deve ser perfilado usando métodos Win32 convencionais. No entanto, o perfilador CLR inclui eventos de transição para determinar os limites entre código gerido e não gerido.
Aplicações automodificáveis que modificam o seu próprio código para fins como programação orientada a aspetos.
Verificação de limites, porque a API de perfilagem não fornece esta informação. O CLR fornece suporte intrínseco para verificação de limites de todo o código gerido.
Perfil remoto, que não é suportado pelas seguintes razões:
O perfil remoto prolonga o tempo de execução. Quando utiliza as interfaces de perfil, deve minimizar o tempo de execução para que os resultados de perfil não sejam indevidamente afetados. Isto é especialmente verdade quando o desempenho da execução está a ser monitorizado. No entanto, o perfil remoto não é uma limitação quando as interfaces de perfilagem são usadas para monitorizar o uso de memória ou para obter informações em tempo de execução sobre frames de pilha, objetos, e assim por diante.
O profiler de código CLR deve registar uma ou mais interfaces de callback com o tempo de execução no computador local onde a aplicação perfilada está a correr. Isto limita a capacidade de criar um profiler de código remoto.
Tópicos de Notificação
Na maioria dos casos, o thread que gera um evento também executa notificações. Tais notificações (por exemplo, FunctionEnter e FunctionLeave) não precisam de fornecer a expressão explícita ThreadID. Além disso, o profiler pode decidir usar armazenamento local de threads para armazenar e atualizar os blocos de análise em vez de indexar os blocos de análise no armazenamento global, com base no ThreadID thread afetado.
Note que estes callbacks não são serializados. Os utilizadores devem proteger o seu código criando estruturas de dados seguras para threads e bloqueando o código do profiler quando necessário para evitar o acesso paralelo de múltiplas threads. Portanto, em certos casos pode receber uma sequência invulgar de callbacks. Por exemplo, suponha que uma aplicação gerida está a gerar dois threads que estão a executar código idêntico. Neste caso, é possível receber um evento ICorProfilerCallback::JITCompilationStarted para uma função de um thread e um FunctionEnter callback do outro thread antes de receber o callback ICorProfilerCallback::JITCompilationFinished . Neste caso, o utilizador receberá um FunctionEnter callback para uma função que pode ainda não ter sido totalmente compilada just-in-time (JIT).
Segurança
Uma DLL profiler é uma DLL não gerida que funciona como parte do motor de execução em tempo de execução da linguagem comum. Como resultado, o código na DLL do profiler não está sujeito às restrições da segurança de acesso ao código gerido. As únicas limitações à DLL do profiler são as impostas pelo sistema operativo ao utilizador que está a executar a aplicação perfilada.
Os autores de perfiladores devem tomar as precauções adequadas para evitar problemas relacionados com a segurança. Por exemplo, durante a instalação, uma DLL de profiler deve ser adicionada a uma lista de controlo de acesso (ACL) para que um utilizador malicioso não a possa modificar.
Combinar Código Gerido e Não Gerido num Profiler de Código
Um perfilador mal escrito pode causar referências circulares a si próprio, resultando em comportamentos imprevisíveis.
Uma análise da API de perfilação CLR pode criar a impressão de que pode escrever um perfilador que contenha componentes geridos e não geridos que se ligam entre si através de interoperação COM ou chamadas indiretas.
Embora isto seja possível do ponto de vista do design, a API de perfilagem não suporta componentes geridos. Um perfilador CLR tem de ser completamente não gerido. Tentativas de combinar código gerido e não gerido num perfilador CLR podem causar violações de acesso, falhas de programa ou bloqueios. Os componentes geridos do perfilador enviam eventos de volta para os seus componentes não geridos, que subsequentemente chamam novamente os componentes geridos, resultando em referências circulares.
A única localização onde um perfilador CLR pode chamar código gerido de forma segura é no corpo da linguagem intermédia comum (CIL) de um método. A prática recomendada para modificar o corpo CIL é utilizar os métodos de recompilação JIT na interface ICorProfilerCallback4 .
Também é possível usar os métodos de instrumentação mais antigos para modificar o CIL. Antes de a compilação just-in-time (JIT) de uma função ser concluída, o profiler pode inserir chamadas geridas no corpo CIL de um método e depois compilá-lo com JIT (ver o método ICorProfilerInfo::GetILFunctionBody ). Esta técnica pode ser usada com sucesso para instrumentação seletiva de código gerido, ou para recolher estatísticas e dados de desempenho sobre o JIT.
Alternativamente, um profiler de código pode inserir ganchos nativos no corpo CIL de cada função gerida que chama código não gerido. Esta técnica pode ser usada para instrumentação e cobertura. Por exemplo, um profile de código poderia inserir ganchos de instrumentação após cada bloco CIL para garantir que o bloco foi executado. A modificação do corpo CIL de um método é uma operação muito delicada, e há muitos fatores que devem ser considerados.
Perfilar Código Não Gerido
A API de perfilação em tempo de execução da linguagem comum (CLR) oferece suporte mínimo para perfilar código não gerido. A seguinte funcionalidade é fornecida:
Enumeração de cadeias de pilha. Esta funcionalidade permite que um profiler de código determine a fronteira entre código gerido e código não gerido.
Determinação se uma cadeia de pilhas corresponde a código gerido ou código nativo.
Nas versões 1.0 e 1.1 do .NET Framework, estes métodos estão disponíveis através do subconjunto em processo da API de depuração CLR. Estão definidos no ficheiro CorDebug.idl.
No .NET Framework 2.0 e posteriores, pode usar o método ICorProfilerInfo2::D oStackSnapshot para esta funcionalidade.
Utilização do COM
Embora as interfaces de perfil sejam definidas como interfaces COM, o runtime de linguagem comum (CLR) não inicializa realmente o COM para usar estas interfaces. A razão é evitar ter de definir o modelo de threading usando a função CoInitialize antes que a aplicação gerida tenha tido a oportunidade de especificar o modelo de threading desejado. De forma semelhante, o próprio profiler não deve chamar CoInitialize, pois pode escolher um modelo de threading incompatível com a aplicação a perfilar e pode causar a falha da aplicação.
Pilha de Chamadas
A API de perfilagem oferece duas formas de obter pilhas de chamadas: um método de snapshot de pilha, que permite a recolha esparsa de pilhas de chamadas, e um método de pilha sombra, que rastreia a pilha de chamadas a cada instante.
Stack Snapshot
Um snapshot de pilha é um rasto da pilha de um thread num instante no tempo. A API de perfilagem suporta o rastreio de funções geridas na pilha, mas deixa o rastreio de funções não geridas ao próprio stack walker do profiler.
Para mais informações sobre como programar o profiler para percorrer stacks geridos, consulte o método ICorProfilerInfo2::D oStackSnapshot neste conjunto de documentação e Profiler Stack Walking no .NET Framework 2.0: Basics and Beyond.
Stack das Sombras
Usar o método snapshot com demasiada frequência pode rapidamente criar um problema de desempenho. Se quiseres fazer stack traces frequentemente, o teu profiler deve antes construir uma stack sombra usando os callbacks de exceção FunctionEnter2, FunctionLeave2, FunctionTailcall2 e ICorProfilerCallback2 . A pilha sombra está sempre presente e pode ser rapidamente copiada para armazenamento sempre que for necessário um snapshot da pilha.
Uma pilha sombra pode obter argumentos de função, valores de retorno e informação sobre instâncias genéricas. Esta informação está disponível apenas através da pilha sombra e pode ser obtida quando o controlo é entregue a uma função. No entanto, esta informação pode não estar disponível mais tarde durante a execução da função.
Chamadas de Retorno e Profundidade de Stack
Os callbacks do profiler podem ser emitidos em circunstâncias muito limitadas à pilha, e um excesso de stack num callback do profiler levará à saída imediata do processo. Um profiler deve garantir que usa o mínimo stack possível em resposta a callbacks. Se o profiler for destinado a ser usado contra processos robustos contra o excesso de pilha, o próprio perfilador deve também evitar desencadear o excesso de pilha.
Tópicos relacionados
| Title | Description |
|---|---|
| Configuração de um Ambiente de Perfilagem | Explica como inicializar um profiler, definir notificações de eventos e perfilar um serviço Windows. |
| Interfaces de criação de perfil | Descreve as interfaces não geridas que a API de perfilagem utiliza. |
| Perfilamento de Funções Estáticas Globais | Descreve as funções globais estáticas não geridas que a API de perfilagem utiliza. |
| Enumerações de Perfil | Descreve as enumerações não geridas que a API de perfilagem utiliza. |
| Estruturas de Perfilação | Descreve as estruturas não geridas que a API de perfilagem utiliza. |