诊断由扩展引起的 UI 延迟
当 UI 无响应时,Visual Studio 会检查 UI 线程的调用堆栈,从叶开始一直到底部。 如果 Visual Studio 确定某个调用堆栈框架属于一个已安装并启用扩展的模块,它就会显示一条通知。
通知会告知用户,UI 延迟(即 UI 无响应)可能是扩展中的代码造成的。 它还为用户提供了禁用扩展或该扩展未来通知的选项。
本文档介绍了如何诊断扩展代码中导致 UI 延迟通知的原因。
注意
不要使用 Visual Studio 试验实例来诊断 UI 延迟。 使用试验实例时,UI 延迟通知所需的调用堆栈分析的某些部分会被关闭,这意味着 UI 延迟通知可能不会显示。
诊断过程概述如下:
- 确定触发方案。
- 重启 VS 并开启活动日志记录。
- 开始 ETW 跟踪。
- 触发通知以再次出现。
- 停止 ETW 跟踪。
- 检查活动日志,以获取延迟 ID。
- 使用步骤 6 中的延迟 ID 来分析 ETW 跟踪。
在以下部分中,我们将详细介绍这些步骤。
确定触发方案
要诊断 UI 延迟,首先需要确定是什么(操作序列)导致 Visual Studio 显示通知。 这样做是为了让你能在稍后打开日志记录时触发通知。
重启 VS 并开启活动日志记录
Visual Studio 可以生成一个“活动日志”,提供有助于调试问题的信息。 要在 Visual Studio 中打开活动日志记录,请使用 /log
命令行选项打开 Visual Studio。 在 Visual Studio 启动后,活动日志会存储在以下位置:
%APPDATA%\Microsoft\VisualStudio\<vs_instance_id>\ActivityLog.xml
要进一步了解如何查找 VS 实例 ID,请参阅用于检测和管理 Visual Studio 实例的工具。 我们稍后将使用此活动日志来查找有关 UI 延迟和相关通知的详细信息。
启动 ETW 跟踪
可以使用 PerfView 来收集 ETW 跟踪。 PerfView 提供了一个易于使用的界面,既可用于收集 ETW 跟踪信息,也可用于对其进行分析。 使用以下命令来收集跟踪:
Perfview.exe collect C:\trace.etl /BufferSizeMB=1024 -CircularMB:2048 -Merge:true -Providers:*Microsoft-VisualStudio:@StacksEnabled=true -NoV2Rundown /kernelEvents=default+FileIOInit+ContextSwitch+Dispatcher
这样将启用“Microsoft-VisualStudio”提供程序,而 Visual Studio 将使用该提供程序来处理与 UI 延迟通知相关的事件。 它还指定了 PerfView 可用于生成“线程时间堆栈”视图的内核提供程序的关键字。
触发通知以再次出现
一旦 PerfView 开始收集跟踪信息,就可以使用触发操作序列(从步骤 1 开始)让通知再次出现。 显示通知后,就可以停止跟踪收集,让 PerfView 处理并生成输出跟踪文件。
停止 ETW 跟踪
要停止跟踪收集,只需使用 PerfView 窗口中的“停止收集”按钮即可。 停止跟踪收集后,PerfView 将自动处理 ETW 事件并生成输出跟踪文件。
检查活动日志,以获取延迟 ID
如前所述,可以在 %APPDATA%\Microsoft\VisualStudio<vs_instance_id>\ActivityLog.xml 中找到活动日志。 Visual Studio 每次检测到扩展 UI 延迟时,都会向活动日志写入一个节点,并将 UIDelayNotifications
作为源代码。 此节点包含有关 UI 延迟的四项信息:
- UI 延迟 ID,这是一个序列号,用于唯一标识 VS 会话中的 UI 延迟
- 会话 ID,它是 Visual Studio 会话从开始到结束的唯一标识
- 是否显示 UI 延迟通知
- 可能导致 UI 延迟的扩展
<entry>
<record>271</record>
<time>2018/02/03 12:02:52.867</time>
<type>Information</type>
<source>UIDelayNotifications</source>
<description>A UI delay (Delay ID = 0) has been detected. (Session ID=16e49d4b-26c2-4247-ad1c-488edeb185e0; Blamed extension="UIDelayR2"; Notification shown? Yes.)</description>
</entry>
注意
并非所有 UI 延迟都会导致通知。 因此,应始终检查“显示的通知?”值,以正确识别正确的 UI 延迟。
在活动日志中找到正确的 UI 延迟后,记下节点中指定的 UI 延迟 ID。 下一步,你将使用 ID 来查找相应的 ETW 事件。
分析 ETW 文件
接下来,打开跟踪文件。 可以使用 PerfView 的同一实例或启动一个新实例,并将窗口左上角的当前文件夹路径设置为跟踪文件的位置。
然后,在左窗格中选择跟踪文件,并从右键单击或上下文菜单中选择“打开”将其打开。
注意
默认情况下,PerfView 会输出一个 Zip 存档。 在打开 trace.zip时,它会自动解压缩存档并打开跟踪。 可以在跟踪收集过程中取消选中 Zip 框,跳过这一步骤。 但是,如果计划在不同的计算机上传输和使用跟踪,则强烈建议不要选中 Zip 框。 如果没有此选项,则 Ngen 程序集所需的 PDB 将不会随跟踪一起提供,因此 Ngen 程序集的符号将无法在目标计算机上解析。 (有关 Ngen 程序集的 PDB 的详细信息,请参阅此博客文章。)
PerfView 处理和打开跟踪可能需要几分钟时间。 打开跟踪后,其下方会出现各种“视图”的列表。
我们首先使用“事件”视图来获取 UI 延迟的时间范围:
- 选择跟踪下的
Events
节点,并从右键单击或上下文菜单中选择“打开”,从而打开“事件”视图。 - 在左窗格中选择“
Microsoft-VisualStudio/ExtensionUIUnresponsiveness
”。 - 按 Enter
在应用选择后,右窗格中将显示所有的 ExtensionUIUnresponsiveness
事件。
右侧窗格中的每一行都对应一个 UI 延迟。 该事件包含一个“延迟 ID”值,该值应与步骤 6 中活动日志中的延迟 ID 一致。 由于 ExtensionUIUnresponsiveness
在 UI 延迟结束时触发,因此事件的时间戳(大致)标志着 UI 延迟的结束时间。 该事件还包含延迟持续时间。 我们可以从结束时间戳中减去持续时间,从而得到 UI 延迟开始的时间戳。
例如,在前面的屏幕截图中,事件的时间戳为 12,125.679,延迟时间为 6,143.085 (ms)。 因此,
- 则延迟开始时间为 12,125.679 - 6,143.085 = 5,982.594。
- UI 延迟时间范围为 5,982.594 至 12,125.679。
获得时间范围后,我们就可以关闭“事件”视图,然后打开“线程事件(包含 StartStop 活动)堆栈”视图。 这个视图特别方便,因为阻止 UI 线程的扩展往往只是在等待其他线程或 I/O 绑定操作。 因此,“CPU 堆栈”视图是大多数情况下的首选,它可能无法捕获线程阻止所花费的时间,因为在此期间线程并未使用 CPU。 “线程时间堆栈”通过正确显示阻止时间解决了这个问题。
在打开“线程时间堆栈”视图时,选择 devenv 进程以开始分析。
在页面左上方的“线程时间堆栈”视图中,可以将时间范围设置为上一步中计算得出的值,然后按 Enter,这样堆栈就会调整为该时间范围。
注意
如果在 Visual Studio 已打开后才开始跟踪收集,那么确定哪个线程是 UI(启动)线程可能会有悖常理。 但是,UI(启动)线程堆栈上的第一个元素很可能总是操作系统 DLL(ntdll.dll 和 kernel32.dll),其次是 devenv!?
,然后是 msenv!?
。 此序列有助于标识 UI 线程。
还可以进一步筛选该视图,只包括包含包中模块的堆栈。
- 将 GroupPats 设置为空文本,以删除默认添加的任何分组。
- 将 IncPats 设置为除现有流程筛选器外,还包括程序集名称的一部分。 在这种情况下,它应该是 devenv;UIDelayR2。
PerfView 在“帮助”菜单下提供了详细的指导,可以使用它来识别代码中的性能瓶颈。 此外,以下链接还提供了有关如何利用 Visual Studio 线程 API 优化代码的详细信息信息:
https://github.com/Microsoft/vs-threading/blob/master/doc/index.md
https://github.com/Microsoft/vs-threading/blob/master/doc/cookbook_vs.md
还可以使用新的 Visual Studio 扩展静态分析器(此处为 NuGet 包),它为编写高效扩展提供了最佳做法指导。 请参阅 VSSDK 分析器和线程分析器列表。
注意
如果由于无法控制的依赖项(例如,如果扩展必须在 UI 线程上调用同步 VS 服务)导致无法响应,我们希望了解相关情况。 如果你是 Visual Studio 合作伙伴计划的成员,则可以通过提交开发人员支持请求与我们联系。 否则,请使用“报告问题”工具提交反馈,并在标题中注明 "Extension UI Delay Notifications"
。 此外,还要提供分析的详细说明。