Coletar informações detalhadas de carregamento de assembly
Do .NET 5 em diante, o runtime pode emitir eventos por meio de EventPipe
com informações detalhadas sobre o carregamento de assembly gerenciado para ajudar no diagnóstico de problemas de carregamento de assembly. Esses eventos são emitidos pelo provedor Microsoft-Windows-DotNETRuntime
sob a palavra-chave AssemblyLoader
(0x4
).
Pré-requisitos
- SDK do .NET 5 ou versões posteriores
- Ferramenta
dotnet-trace
Observação
O escopo de funcionalidades de dotnet-trace
é maior do que coletar informações detalhadas de carregamento de assembly. Para obter mais informações sobre o uso de dotnet-trace
, confira dotnet-trace
.
Coletar um rastreamento com eventos de carregamento de assembly
Você pode usar dotnet-trace
para rastrear um processo existente ou iniciar um processo filho e rastreá-lo desde a inicialização.
Rastrear um processo existente
Para habilitar eventos de carregamento de assembly em tempo de execução e coletar um rastreamento deles, use dotnet-trace
com o seguinte comando:
dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 --process-id <pid>
Esse comando coleta um rastreamento do <pid>
especificado habilitando os eventos de AssemblyLoader
no provedor Microsoft-Windows-DotNETRuntime
. O resultado é um arquivo .nettrace
.
Usar o dotnet-trace para iniciar um processo filho e rastreá-lo desde a inicialização
Às vezes, pode ser útil coletar o rastreamento de um processo desde a inicialização. Para aplicativos que executam o .NET 5 ou posterior, você pode usar dotnet-trace
para fazer isso.
O seguinte comando inicia hello.exe com arg1
e arg2
como argumentos de linha de comando e coleta um rastreamento desde a inicialização em tempo de execução:
dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 -- hello.exe arg1 arg2
Você pode parar de coletar o rastreamento pressionando Enter ou Ctrl + C. Isso também fecha o hello.exe.
Observação
- A inicialização de hello.exe por meio de
dotnet-trace
redireciona as respectivas entradas e saídas e você não pode interagir com ele no console por padrão. Use a opção--show-child-io
para interagir com as respectivasstdin
estdout
. - O encerramento da ferramenta por meio de Ctrl+C ou
SIGTERM
encerra com segurança a ferramenta e o processo filho. - Se o processo filho for encerrado antes da ferramenta, a ferramenta também será encerrada e o rastreamento será exibido com segurança.
Exibir um rastreamento
O arquivo de rastreamento coletado pode ser exibido no Windows usando a exibição Eventos no PerfView. Todos os eventos de carregamento de assembly serão prefixados com Microsoft-Windows-DotNETRuntime/AssemblyLoader
.
Exemplo (no Windows)
Este exemplo usa a amostra de pontos de extensão de carregamento de assembly. O aplicativo tenta carregar um assembly MyLibrary
– Um assembly que não é referenciado pelo aplicativo e, portanto, requer o processamento de um ponto de extensão de carregamento de assembly para que ele seja carregado com êxito.
Coletar o rastreamento
Navegue até o diretório com o exemplo baixado. Compile o aplicativo com:
dotnet build
Inicie o aplicativo com argumentos que indicam que ele deve ser pausado, aguardando um pressionamento de tecla. Na retomada, ele tentará carregar o assembly no padrão
AssemblyLoadContext
– Sem o processamento necessário para o sucesso do carregamento. Acesse o diretório de saída e execute:AssemblyLoading.exe /d default
Localize a ID do processo de aplicativo.
dotnet-trace ps
A saída listará os processos disponíveis. Por exemplo:
35832 AssemblyLoading C:\src\AssemblyLoading\bin\Debug\net5.0\AssemblyLoading.exe
Anexe
dotnet-trace
ao aplicativo em execução.dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 --process-id 35832
Na janela que executa o aplicativo, pressione qualquer tecla para permitir que o programa continue. O rastreamento será interrompido automaticamente quando o aplicativo for encerrado.
Exibir o rastreamento
Abra o rastreamento coletado no PerfView e abra a exibição Eventos. Filtre a lista de eventos para eventos de Microsoft-Windows-DotNETRuntime/AssemblyLoader
.
Todos os carregamentos de assembly que ocorreram no aplicativo após o rastreamento iniciado serão mostrados. Para inspecionar a operação de carregamento do assembly deste exemplo, MyLibrary
, podemos aplicar mais filtragens.
Carregamentos de assembly
Filtre a exibição para os eventos Start
e Stop
em Microsoft-Windows-DotNETRuntime/AssemblyLoader
usando a lista de eventos à esquerda. Adicione as colunas AssemblyName
, ActivityID
e Success
à exibição. Filtre eventos que contenham MyLibrary
.
Nome do evento | AssemblyName | ActivityID | Sucesso |
---|---|---|---|
AssemblyLoader/Start |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | |
AssemblyLoader/Stop |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | Falso |
Você verá um par de Start
/Stop
com Success=False
no evento Stop
, indicando que a operação de carregamento falhou. Observe que os dois eventos têm a mesma ID de atividade. A ID da atividade pode ser usada para filtrar todos os outros eventos do carregador de assembly apenas aos correspondentes a essa operação de carregamento.
Detalhamento da tentativa de carregamento
Para obter um detalhamento maior da operação de carregamento, filtre a exibição para os eventos ResolutionAttempted
em Microsoft-Windows-DotNETRuntime/AssemblyLoader
usando a lista de eventos à esquerda. Adicione as colunas AssemblyName
, Stage
e Result
à exibição. Filtre eventos com a ID da atividade do par Start
/Stop
.
Nome do evento | AssemblyName | Estágio | Result |
---|---|---|---|
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
FindInLoadContext |
AssemblyNotFound |
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
ApplicationAssemblies |
AssemblyNotFound |
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
AssemblyLoadContextResolvingEvent |
AssemblyNotFound |
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
AppDomainAssemblyResolveEvent |
AssemblyNotFound |
Os eventos acima indicam que o carregador de assembly tentou resolver o assembly examinando o contexto de carregamento atual, executando a lógica de investigação padrão para assemblies de aplicativos gerenciados, invocando manipuladores para o evento AssemblyLoadContext.Resolving e invocando manipuladores para o AppDomain.AssemblyResolve. Para todas essas etapas, o assembly não foi encontrado.
Pontos de extensão
Para ver quais pontos de extensão foram invocados, filtre a exibição para o AssemblyLoadContextResolvingHandlerInvoked
e AppDomainAssemblyResolveHandlerInvoked
em Microsoft-Windows-DotNETRuntime/AssemblyLoader
usando a lista de eventos à esquerda. Adicione as colunas AssemblyName
e HandlerName
à exibição. Filtre eventos com a ID da atividade do par Start
/Stop
.
Nome do evento | AssemblyName | HandlerName |
---|---|---|
AssemblyLoader/AssemblyLoadContextResolvingHandlerInvoked |
MyLibrary, Culture=neutral, PublicKeyToken=null |
OnAssemblyLoadContextResolving |
AssemblyLoader/AppDomainAssemblyResolveHandlerInvoked |
MyLibrary, Culture=neutral, PublicKeyToken=null |
OnAppDomainAssemblyResolve |
Os eventos acima indicam que um manipulador chamado OnAssemblyLoadContextResolving
foi invocado para o evento AssemblyLoadContext.Resolving e um manipulador chamado OnAppDomainAssemblyResolve
foi invocado para o evento AppDomain.AssemblyResolve.
Coletar outro rastreamento
Execute o aplicativo com argumentos para que o manipulador do evento AssemblyLoadContext.Resolving carregue o assembly MyLibrary
.
AssemblyLoading /d default alc-resolving
Colete e abra outro arquivo .nettrace
seguindo as etapas acima.
Filtre apenas os eventos Start
e Stop
para MyLibrary
novamente. Você verá um par Start
/Stop
com outro Start
/Stop
entre eles. A operação interna de carregamento representa o carregamento disparado pelo manipulador para AssemblyLoadContext.Resolving quando ele chamou AssemblyLoadContext.LoadFromAssemblyPath. Desta vez, você verá Success=True
no evento Stop
, indicando que a operação de carregamento foi bem-sucedida. O campo ResultAssemblyPath
mostra o caminho do assembly resultante.
Nome do evento | AssemblyName | ActivityID | Sucesso | ResultAssemblyPath |
---|---|---|---|---|
AssemblyLoader/Start |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | ||
AssemblyLoader/Start |
MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null |
//1/2/1/ | ||
AssemblyLoader/Stop |
MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null |
//1/2/1/ | True | C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll |
AssemblyLoader/Stop |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | True | C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll |
Depois, podemos examinar os eventos ResolutionAttempted
com a ID da atividade do carregamento externo para determinar a etapa na qual o assembly foi resolvido com êxito. Desta vez, os eventos mostrarão que a fase AssemblyLoadContextResolvingEvent
foi bem sucedida. O campo ResultAssemblyPath
mostra o caminho do assembly resultante.
Nome do evento | AssemblyName | Estágio | Result | ResultAssemblyPath |
---|---|---|---|---|
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
FindInLoadContext |
AssemblyNotFound |
|
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
ApplicationAssemblies |
AssemblyNotFound |
|
AssemblyLoader/ResolutionAttempted |
MyLibrary, Culture=neutral, PublicKeyToken=null |
AssemblyLoadContextResolvingEvent |
Success |
C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll |
Ao examinar os eventos AssemblyLoadContextResolvingHandlerInvoked
, veja que o manipulador chamado OnAssemblyLoadContextResolving
foi invocado. O campo ResultAssemblyPath
mostra o caminho do assembly retornado pelo manipulador.
Nome do evento | AssemblyName | HandlerName | ResultAssemblyPath |
---|---|---|---|
AssemblyLoader/AssemblyLoadContextResolvingHandlerInvoked |
MyLibrary, Culture=neutral, PublicKeyToken=null |
OnAssemblyLoadContextResolving |
C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll |
Observe que não há mais um evento ResolutionAttempted
com a fase AppDomainAssemblyResolveEvent
nem eventos AppDomainAssemblyResolveHandlerInvoked
, pois o assembly foi carregado com êxito antes de chegar à fase do algoritmo de carregamento que gera o evento AppDomain.AssemblyResolve.