收集详细的程序集加载信息

自 .NET 5 起,运行时可以通过 EventPipe 发出事件,其中包含关于托管程序集加载的详细信息,以帮助诊断程序集加载问题。 这些事件是由 Microsoft-Windows-DotNETRuntime 提供程序在 AssemblyLoader 关键字 (0x4) 下发出的。

先决条件

注意

dotnet-trace 功能的范围不仅限于收集详细的程序集加载信息。 有关 dotnet-trace 用法的详细信息,请参阅 dotnet-trace

收集包含程序集加载事件的跟踪

可以使用 dotnet-trace 跟踪现有进程或启动子进程并从启动中跟踪它。

跟踪现有进程

若要在运行时中启用程序集加载事件并收集它们的跟踪,请使用 dotnet-trace 和以下命令:

dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 --process-id <pid>

此命令将收集指定 <pid> 的跟踪,从而在 Microsoft-Windows-DotNETRuntime 提供程序中启用 AssemblyLoader 事件。 结果是 .nettrace 文件。

使用 dotnet-trace 启动子进程并从启动中跟踪它

有时,从进程启动中收集进程的跟踪可能很有用。 对于运行 .NET 5 或更高版本的应用,可以使用 dotnet-trace 来执行此操作。

以下命令以 arg1arg2 作为其命令行参数启动 hello.exe,并从其运行时启动收集跟踪:

dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 -- hello.exe arg1 arg2

可以通过按 EnterCtrl + C 来停止收集跟踪。这也会关闭 hello.exe。

注意

  • 通过 dotnet-trace 启动 hello.exe 会重定向其输入和输出;默认情况下,你将无法在控制台上与其交互。 使用 --show-child-io 开关与其 stdinstdout 进行交互。
  • 通过 Ctrl+CSIGTERM 退出工具将正常地结束该工具和子进程。
  • 如果子进程在工具之前退出,工具也将退出,应可安全查看跟踪。

查看跟踪

可以使用 PerfView中的“事件”视图在 Windows 上查看所收集的跟踪文件。 所有程序集加载事件都将以 Microsoft-Windows-DotNETRuntime/AssemblyLoader 为前缀。

示例(在 Windows 上)

本例使用程序集加载扩展点示例。 应用程序尝试加载程序集 MyLibrary(应用程序未引用的程序集),因此需要在程序集加载扩展点中进行处理才能成功加载。

收集跟踪

  1. 导航到包含下载的示例的目录。 通过以下方式生成应用程序:

    dotnet build
    
  2. 使用指明应用程序应暂停的参数来启动应用程序,并等待按键。 恢复后,它将尝试在默认 AssemblyLoadContext 中加载程序集,不需要进行成功加载所需的处理。 导航到输出目录并运行:

    AssemblyLoading.exe /d default
    
  3. 找到应用程序的进程 ID。

    dotnet-trace ps
    

    输出将列出可用进程。 例如: 。

    35832 AssemblyLoading C:\src\AssemblyLoading\bin\Debug\net5.0\AssemblyLoading.exe
    
  4. dotnet-trace 附加到正在运行的应用程序。

    dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 --process-id 35832
    
  5. 在运行应用程序的窗口中,按任意键让程序继续运行。 一旦应用程序退出,跟踪将自动停止。

查看跟踪

PerfView 中打开所收集的跟踪,然后打开“事件”视图。 将“事件”列表筛选为 Microsoft-Windows-DotNETRuntime/AssemblyLoader 事件。

PerfView assembly loader filter image

将显示在跟踪启动后应用程序中发生的所有程序集加载。 若要检查此示例中关注的程序集 (MyLibrary) 的加载操作,可以执行更多筛选。

程序集加载

使用左侧的“事件”列表,将视图筛选为 下的 StartMicrosoft-Windows-DotNETRuntime/AssemblyLoaderStop 事件。 将列 AssemblyNameActivityIDSuccess 添加到视图中。 筛选为包含 MyLibrary 的事件。

PerfView Start and Stop events image

事件名称 AssemblyName ActivityID Success
AssemblyLoader/Start MyLibrary, Culture=neutral, PublicKeyToken=null //1/2/
AssemblyLoader/Stop MyLibrary, Culture=neutral, PublicKeyToken=null //1/2/ False

你应该会在 Stop 事件上看到一个带有 Success=FalseStart/Stop 对,表明加载操作失败。 请注意,这两个事件有相同的活动 ID。 活动 ID 可用于将其他所有程序集加载程序事件筛选为仅与此加载操作相对应的事件。

加载尝试明细

有关加载操作的更详细明细,请使用左侧的“事件”列表,将视图筛选为 Microsoft-Windows-DotNETRuntime/AssemblyLoader 下的 ResolutionAttempted 事件。 将列 AssemblyNameStageResult 添加到视图中。 筛选为包含 Start/Stop 对中的活动 ID 的事件。

PerfView ResolutionAttempted events image

事件名称 AssemblyName 阶段 结果
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

上面的事件表明,程序集加载程序试图通过以下方式解析程序集:在当前加载上下文中查找、运行托管应用程序程序集的默认探测逻辑、调用 AssemblyLoadContext.Resolving 事件的处理程序,以及调用 AppDomain.AssemblyResolve 的处理程序。 对于所有这些步骤,都没有找到程序集。

扩展点

若要查看调用了哪些扩展点,请使用左侧的“事件”列表,将视图筛选为 下的 AssemblyLoadContextResolvingHandlerInvokedMicrosoft-Windows-DotNETRuntime/AssemblyLoaderAppDomainAssemblyResolveHandlerInvoked。 将列 AssemblyNameHandlerName 添加到视图中。 筛选为包含 Start/Stop 对中的活动 ID 的事件。

PerfView extension point events image

事件名称 AssemblyName HandlerName
AssemblyLoader/AssemblyLoadContextResolvingHandlerInvoked MyLibrary, Culture=neutral, PublicKeyToken=null OnAssemblyLoadContextResolving
AssemblyLoader/AppDomainAssemblyResolveHandlerInvoked MyLibrary, Culture=neutral, PublicKeyToken=null OnAppDomainAssemblyResolve

上面的事件表明,为 AssemblyLoadContext.Resolving 事件调用了名为 OnAssemblyLoadContextResolving 的处理程序,为 AppDomain.AssemblyResolve 事件调用了名为 OnAppDomainAssemblyResolve 的处理程序。

收集另一个跟踪

使用参数运行应用程序,以便 AssemblyLoadContext.Resolving 事件的处理程序将加载 MyLibrary 程序集。

AssemblyLoading /d default alc-resolving

收集并使用上面的步骤打开另一个 .nettrace 文件。

再次筛选为 MyLibraryStartStop 事件。 你应该会看到 Start/Stop 对,以及另一个 Start/Stop 介于二者之间。 内部加载操作表示 AssemblyLoadContext.Resolving 的处理程序在调用 AssemblyLoadContext.LoadFromAssemblyPath 时触发的加载。 这一次,你应该会在 Stop 事件上看到 Success=True,表明加载操作成功。 ResultAssemblyPath 字段显示生成的程序集的路径。

PerfView successful Start and Stop events image

事件名称 AssemblyName ActivityID Success 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

然后,我们可以查看带有外部负载的活动 ID 的 ResolutionAttempted 事件,以确定成功解析程序集的步骤。 这一次,事件会显示 AssemblyLoadContextResolvingEvent 阶段已成功完成。 ResultAssemblyPath 字段显示生成的程序集的路径。

PerfView successful ResolutionAttempted events image

事件名称 AssemblyName 阶段 结果 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

查看 AssemblyLoadContextResolvingHandlerInvoked 事件将看到调用了名为 OnAssemblyLoadContextResolving 的处理程序。 ResultAssemblyPath 字段显示由处理程序返回的程序集的路径。

PerfView successful extension point events image

事件名称 AssemblyName HandlerName ResultAssemblyPath
AssemblyLoader/AssemblyLoadContextResolvingHandlerInvoked MyLibrary, Culture=neutral, PublicKeyToken=null OnAssemblyLoadContextResolving C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll

请注意,不再存在带有 AppDomainAssemblyResolveEvent 阶段的 ResolutionAttempted 事件或任何 AppDomainAssemblyResolveHandlerInvoked 事件,因为程序集在到达引发 AppDomain.AssemblyResolve 事件的加载算法步骤之前已成功加载。

另请参阅