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

从 .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> 的跟踪,从而在 AssemblyLoader 提供程序中启用 Microsoft-Windows-DotNETRuntime 事件。 结果是一个 .nettrace 文件。

使用 dotnet-trace 启动子进程并从启动时对其进行跟踪

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

以下命令启动 hello.exe,并将 arg1arg2 作为命令行参数,同时从其运行时启动过程中收集跟踪信息。

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

可以按 EnterCtrl + C 停止收集痕迹。这也关闭了 hello.exe

注释

  • 通过 启动 hello.exe 会重定向其输入和输出;默认情况下,你将无法在控制台上与其交互dotnet-trace。 使用 --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 程序集加载过滤器图像

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

程序集加载

使用左侧的事件列表将视图筛选为Start下的StopMicrosoft-Windows-DotNETRuntime/AssemblyLoader事件。 将列AssemblyNameActivityIDSuccess添加到视图中。 筛选至包含MyLibrary的事件。

PerfView 启动和停止事件图像

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

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

加载尝试明细

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

PerfView ResolutionAttempted 事件图像

事件名称 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事件处理程序来解析程序集。 对于所有这些步骤,都没有找到程序集。

扩展点

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

PerfView 扩展点事件图像

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

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

收集另一个跟踪

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

AssemblyLoading /d default alc-resolving

使用.nettrace收集并打开另一个文件。

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

PerfView 成功启动和停止事件图像

事件名称 AssemblyName ActivityID 成功 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/ 真 实 C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll
AssemblyLoader/Stop MyLibrary, Culture=neutral, PublicKeyToken=null //1/2/ 真 实 C:\src\AssemblyLoading\bin\Debug\net5.0\MyLibrary.dll

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

PerfView 成功 ResolutionAttempted 事件图像

事件名称 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 成功扩展点事件图像

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

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

另请参阅