从 .NET 5 开始,运行时可以通过 EventPipe
发出事件,并且提供有关 托管程序集加载 的详细信息,以帮助诊断程序集加载问题。 这些事件由Microsoft-Windows-DotNETRuntime
提供程序在AssemblyLoader
关键字(0x4
)下发出。
先决条件
- .NET 5 SDK 或更高版本
-
dotnet-trace
工具
注释
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,并将 arg1
和 arg2
作为命令行参数,同时从其运行时启动过程中收集跟踪信息。
dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 -- hello.exe arg1 arg2
可以按 Enter 或 Ctrl + C 停止收集痕迹。这也关闭了 hello.exe。
注释
- 通过 启动 hello.exe 会重定向其输入和输出;默认情况下,你将无法在控制台上与其交互
dotnet-trace
。 使用--show-child-io
开关与其stdin
和stdout
进行交互。 - 通过 Ctrl+C 或
SIGTERM
退出工具,可安全地结束该工具和子进程。 - 如果子进程在工具之前退出,那么工具也会退出,并且可以安全地查看跟踪结果。
查看跟踪
可以使用 PerfView 中的“事件”视图在 Windows 上查看收集的跟踪文件。 所有程序集加载事件都将带有 Microsoft-Windows-DotNETRuntime/AssemblyLoader
前缀。
示例(在 Windows 上)
此示例使用 程序集加载扩展点示例。 应用程序尝试加载程序集 MyLibrary
- 应用程序未引用的程序集,因此需要在程序集加载扩展点中进行处理才能成功加载。
收集跟踪
导航至包含下载示例的目录。 使用以下方法生成应用程序:
dotnet build
使用指明应用程序应暂停的参数来启动应用程序,并等待按键。 在恢复时,它将尝试在默认值
AssemblyLoadContext
中加载程序集,而无需成功加载所需的处理。 导航到输出目录并运行:AssemblyLoading.exe /d default
查找应用程序的进程 ID。
dotnet-trace ps
输出将列出可用的进程。 例如:
35832 AssemblyLoading C:\src\AssemblyLoading\bin\Debug\net5.0\AssemblyLoading.exe
连接
dotnet-trace
到正在运行的应用程序。dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 --process-id 35832
在运行应用程序的窗口中,按任意键让程序继续。 应用程序退出后,跟踪将自动停止。
查看追踪
在 PerfView 中打开收集的跟踪并打开“事件”视图。 将事件列表筛选为 Microsoft-Windows-DotNETRuntime/AssemblyLoader
事件。
将显示在跟踪启动后应用程序中发生的所有程序集加载。 若要检查此示例中感兴趣的程序集的加载操作 - MyLibrary
,我们可以执行更多筛选。
程序集加载
使用左侧的事件列表将视图筛选为Start
下的Stop
和Microsoft-Windows-DotNETRuntime/AssemblyLoader
事件。 将列AssemblyName
、ActivityID
和Success
添加到视图中。 筛选至包含MyLibrary
的事件。
事件名称 | AssemblyName | ActivityID | 成功 |
---|---|---|---|
AssemblyLoader/Start |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | |
AssemblyLoader/Stop |
MyLibrary, Culture=neutral, PublicKeyToken=null |
//1/2/ | 假 |
你应该会在 Start
事件上看到一个带有 / 的 Stop
Success=False
Stop
对,表明加载操作失败。 请注意,这两个事件具有相同的活动 ID。 活动 ID 可用于筛选其他所有程序集加载事件,仅筛选与此加载操作相关的事件。
加载尝试明细
有关加载操作的更详细明细,请使用左侧的“事件”列表,将视图筛选为 ResolutionAttempted
下的 Microsoft-Windows-DotNETRuntime/AssemblyLoader
。 将列AssemblyName
、Stage
和Result
添加到视图中。 筛选为包含 Start
/Stop
对中的活动 ID 的事件。
事件名称 | 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
下的 AppDomainAssemblyResolveHandlerInvoked
和 Microsoft-Windows-DotNETRuntime/AssemblyLoader
的视图。 将列 AssemblyName
和 HandlerName
添加到视图中。 筛选为包含 Start
/Stop
对中的活动 ID 的事件。
事件名称 | 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
再次筛选为 Start
的 Stop
和 MyLibrary
事件。 你应该会看到 Start
/Stop
对,以及另一个 Start
/Stop
介于二者之间。 内部加载操作表示 AssemblyLoadContext.Resolving 的处理程序在调用 AssemblyLoadContext.LoadFromAssemblyPath 时触发的加载。 这一次,你应该在Success=True
事件中看到Stop
,这表明加载操作成功。 该 ResultAssemblyPath
字段显示生成的程序集的路径。
事件名称 | 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
字段显示生成的程序集的路径。
事件名称 | 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
字段显示处理程序返回的程序集的路径。
事件名称 | 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 事件的加载算法步骤之前已成功加载。