Winscope 是一款 Web 工具,可以让用户在动画和转换期间和之后记录、重放和分析多个系统服务的状态。Winscope 将所有相关的系统服务状态记录在一个跟踪文件中。使用带有跟踪文件的 Winscope 界面,您可以通过重放、单步执行和调试过渡来针对每个动画帧检查这些服务的状态(无论是否有屏幕录制)。
支持的跟踪记录
Winscope 可用于收集和直观呈现各种跟踪记录或系统服务状态序列。您可以根据特定用例配置这些跟踪记录,从低开销到高详细程度。Winscope 支持以下跟踪记录:
- EventLog:使用 EventLog收集系统诊断事件记录。在 Winscope 中,此信息仅用于识别和显示 CUJ 标记。
- IME:来自输入法 (IME) 管道的跟踪事件,包括 IMS、IMMS 和 IME 客户端。
- 输入:跟踪输入事件管道的各个部分中的输入事件。
- ProtoLog:收集来自系统服务的 ProtoLog 消息以及在客户端进程中运行的系统服务的代码。
- 屏幕录制:收集跟踪记录和屏幕录制。
- Shell 过渡:记录窗口和 activity 过渡系统详情。
- SurfaceFlinger:收集包含有关 surface(图层)的信息的 SurfaceFlinger 跟踪记录,例如位置、缓冲区和组合。
- 事务:使用 SurfaceControl跟踪 SurfaceFlinger 收到的用于组合的一系列原子更改。
- ViewCapture:捕获支持 ViewCapture 的系统窗口(例如系统界面和启动器)中所有视图的一系列属性。
- 窗口管理器:跟踪窗口管理器状态,其中包含与窗口相关的详细信息,包括输入和聚焦事件、屏幕方向、过渡、动画、定位和转换。
支持的转储
Winscope 可以收集和显示状态转储,即在用户定义的特定时刻拍摄的设备状态快照。跟踪记录在设备使用期间持续收集,可能会影响性能,而转储仅在这些用户定义的时刻收集,从而确保性能和详细程度不会受到影响。这样可以更有针对性地高效分析设备在特定时间点的状态。Winscope 支持以下转储:
- 窗口管理器:转储单个窗口管理器状态。
- SurfaceFlinger:转储单个 SurfaceFlinger 快照。
- 屏幕截图:收集屏幕截图以及转储文件。
资源
如需了解如何构建和运行 Winscope,请参阅运行 Winscope。
如需了解如何收集跟踪记录,请参阅捕获跟踪记录。
如需了解如何使用 Winscope 网页界面加载跟踪记录,请参阅加载跟踪记录。
如需了解如何分析跟踪记录,请参阅分析跟踪记录。
示例
以下示例介绍了如何调试闪烁测试失败问题和用户报告的 bug。
闪烁测试失败
此示例演示了如何使用 Winscope 调试闪烁测试失败问题。
检查测试失败情况
请按照以下步骤确定问题类型并检查测试失败消息。
- 通过检查测试和类名称来确定问题类型。 - 测试和类名称: - FlickerTestsNotification com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest#appLayerBecomesVisible[ROTATION_0_GESTURAL_NAV]- 问题类型: - CUJ 是指通过锁屏通知启动应用 ( - OpenAppFromLockscreenNotificationColdTest)。
- 测试预计应用会变为可见 ( - #appLayerBecomesVisible)。
 
- 检查测试失败消息,其中包含有关失败的全面信息,包括: - 预期结果与实际可见结果之间的比较
- 时间戳,可帮助您查明发生失败问题的时间
- 与失败相关的制品或文件的名称
- 与理解和调试失败问题相关的其他背景信息
 - android.tools.flicker.subject.exceptions.IncorrectVisibilityException: com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity# should be visible Where? Timestamp(UNIX=2024-05-10T11:04:14.227572545(1715339054227572545ns), UPTIME=37m21s184ms79178ns(2241184079178ns), ELAPSED=0ns) What? Expected: com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity# Actual: [e636ecd com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity#3457: Buffer is empty, Visible region calculated by Composition Engine is empty, com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity#3458: Visible region calculated by Composition Engine is empty] Other information Artifact: FAIL__OpenAppFromLockscreenNotificationColdTest_ROTATION_0_GESTURAL_NAV.zip Check the test run artifacts for trace files at android.tools.flicker.subject.layers.LayerTraceEntrySubject.isVisible(LayerTraceEntrySubject.kt:187) at android.tools.flicker.subject.layers.LayersTraceSubject$isVisible$1$1.invoke(LayersTraceSubject.kt:151) at android.tools.flicker.subject.layers.LayersTraceSubject$isVisible$1$1.invoke(LayersTraceSubject.kt:150) at android.tools.flicker.assertions.NamedAssertion.invoke(NamedAssertion.kt:32) at android.tools.flicker.assertions.CompoundAssertion.invoke(CompoundAssertion.kt:42) at android.tools.flicker.assertions.AssertionsChecker.test(AssertionsChecker.kt:79) at android.tools.flicker.subject.FlickerTraceSubject.forAllEntries(FlickerTraceSubject.kt:59) at android.tools.flicker.assertions.AssertionDataFactory$createTraceAssertion$closedAssertion$1.invoke(AssertionDataFactory.kt:46) at android.tools.flicker.assertions.AssertionDataFactory$createTraceAssertion$closedAssertion$1.invoke(AssertionDataFactory.kt:43) at android.tools.flicker.assertions.AssertionDataImpl.checkAssertion(AssertionDataImpl.kt:33) at android.tools.flicker.assertions.ReaderAssertionRunner.doRunAssertion(ReaderAssertionRunner.kt:35) at android.tools.flicker.assertions.ReaderAssertionRunner.runAssertion(ReaderAssertionRunner.kt:29) at android.tools.flicker.assertions.BaseAssertionRunner.runAssertion(BaseAssertionRunner.kt:36) at android.tools.flicker.legacy.LegacyFlickerTest.doProcess(LegacyFlickerTest.kt:59) at android.tools.flicker.assertions.BaseFlickerTest.assertLayers(BaseFlickerTest.kt:89) at com.android.server.wm.flicker.notification.OpenAppTransition.appLayerBecomesVisible_coldStart(OpenAppTransition.kt:51) at com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest.appLayerBecomesVisible(OpenAppFromNotificationColdTest.kt:64)- 此输出示例表明: - 问题发生在 - 2024-05-10T11:04:14.227572545。
- 预计 - NotificationActivity可见,但实际上不可见。
- 包含用于调试的跟踪记录的制品文件的名称为 - FAIL__OpenAppFromLockscreenNotificationColdTest_ROTATION_0_GESTURAL_NAV。
 
调试
请按以下步骤确定闪烁的原因:
- 下载跟踪记录文件,并在 Winscope 中加载这些文件。Winscope 会打开,并自动选择 SurfaceFlinger:   - 图 1. 包含 SurfaceFlinger 视图的 Winscope 着陆页。 
- 将异常消息中的时间戳复制并粘贴到时间戳字段中,以找到问题发生时的时间戳。您可以复制人类可读格式 ( - 2024-05-10T11:04:14.227572545) 的时间戳并粘贴到第一个字段,也可以复制纳秒级时间戳 (- 1715339054227572545ns) 并粘贴到第二个字段。  - 图 2. 时间戳对话框。 
- 按向左键可前往上一个帧。在此状态下,NotificationActivity 应用会在视频中正确显示,并且应用和启动画面 surface 均可见,这由 3D 视图中的绿色矩形以及其层次结构元素上的 V 条状标签所指示。 - 应用和启动画面 surface 名称如下: - com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity#3458` Splash Screen com.android.server.wm.flicker.testapp#3453- 这表示应用在屏幕变黑时启动,并且此事件发生在应用启动期间,因为启动画面仍可见:   - 图 3. 在应用启动时。 
- 按向右键可返回到下一个闪烁的帧。在矩形视图中,屏幕上显示的是 - NotificationShade,而不是应用。此帧中显示以下 surface:- 屏幕装饰叠加层(顶部和底部)
- 导航栏
- 指针位置(来自屏幕录制)   - 图 4. 闪烁 activity。 
 
- 在层次结构视图中选择应用 activity。如果找不到,请取消选中 Show only V。然后,检查属性视图。 - 应用 surface 名称为: - com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity#3458`  - 图 5. 应用属性。 - 虽然应用 activity 已设置为可见和不透明,但由于 - Invisible due to: null visible region错误,surface 未显示。之所以会出现这种情况,是因为在组合过程中,另一个不透明的 surface 被放置在它前面。此假设源于 3D 视图中- NotificationShade矩形位于- NotificationActivity矩形前面,并且可见的(绿色)- NotificationShade可能是所选图层。
- 为了验证这一假设,请选择当前帧上的可见 - NotificationShadesurface 并检查其属性。标志设置为- OPAQUE|ENABLE_BACKPRESSURE (0x102)。- NotificationShadesurface 名称为- NotificationShade#3447。接下来,按左箭头返回到上一个画面(闪烁之前),然后再次检查- NotificationShadesurface 的属性。请注意,surface 仅具有- ENABLE_BACKPRESSURE (0x100)标志,而不是- OPAQUE。这可以确认- NotificationShade在应用启动完全结束之前变为不透明。由于- NotificationShade位于- NotificationActivity前面,因此应用不会显示。- NotificationShade为黑色,因此屏幕会短暂变黑,从而导致闪烁。
- 在代码中找出 - NotificationShade过早变暗的原因。
用户报告的 bug
用户报告的 bug 通常缺少详细信息,因此很难进行调试。不同于闪烁测试失败(提供特定时间戳、元素详情和屏幕录制内容),用户报告的 bug 通常只包含问题的简要说明。
在我们的案例研究中,唯一提供的信息是标题“Screen flickered when reopening app from split screen” 和大致时间戳“Apr 18, 2024 3:51 PM GMT-04:00”。
请按以下步骤对用户报告的 bug 进行调试:
- 在 Winscope 中加载跟踪文件。Winscope 打开时会自动选择 SurfaceFlinger。   - 图 6. 包含 SurfaceFlinger 视图的 Winscope 着陆页。 
- 在人类可读的时间戳字段中输入 - 15:50:00,以找到用户报告的大致时间戳(在本例中为- 3:50 PM GMT-04:00)。  - 图 7. 时间戳对话框。 
- 使用矩形视图可识别屏幕上绘制的内容。如需获得更好的视图,请使用“旋转”滑块更改矩形视角。在层次结构视图中标记 Show only V 和 Flat 后,壁纸、屏幕装饰叠加层、信箱、启动器、联系人和拨号器 surface 会显示出来。 - 软件包名称如下: - 启动器: - com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#40602
- 联系人: - com.google.android.contacts/com.android.contacts.activities.PeopleActivity#40565
- 拨号器: - com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity#40564
 - 除了可见 surface(绿色矩形)之外,还会显示一个灰色矩形,该矩形代表名为“Unknown display”的显示区域 surface。如需提高可见度,请点击 - ScreenDecorHwcOverlay#64surface 旁边的 ( ) 以隐藏其对应的矩形,并显示后面的 surface。我们移除了分析的叠加层,因为它对用户不可见,也不会被报告为闪烁动画。 ) 以隐藏其对应的矩形,并显示后面的 surface。我们移除了分析的叠加层,因为它对用户不可见,也不会被报告为闪烁动画。  - 图 8. 用户报告。 
- 确定分屏视图中涉及哪些 surface 后,请使用“过渡”跟踪记录来逐步执行各种用户操作并查找闪烁。点击 Winscope 中的过渡标签页,以直观呈现已播放的过渡的列表:   - 图 9. 过渡。 - 在此帧中播放的过渡以蓝色突出显示。在本例中,过渡标志包括 - TRANSIT_FLAG_IS_RECENTS,表示用户正在进入最近使用的界面。
- 点击 Dispatch Time 列(在本例中为 - 2024-04-18, 15:50:57.205)上的链接,前往相应时间点,然后在 Surface Flinger 标签页中验证矩形。通过使用向右箭头键逐步过渡并观察矩形,确认设备在过渡期间的状态是否正确。- 启动器在 15:50:57.278 时出现,但动画不会在该时间开始。壁纸已经可见,因为在分屏应用(分隔线)之间没有绘制任何内容。在前一个帧 (15:50:57.212) 中,壁纸不可见,并且显示了分隔线,这是未进行动画处理时分屏的显示效果。   - 图 10. 闪烁事件发生前的屏幕。 
- 如需查看下一个过渡,请直接点击时间轴。SurfaceFlinger 状态由一行浅蓝色块表示。过渡由一行粉色块表示。   - 图 11. 第一个过渡结束。 - 点击下一个过渡的起始位置处的 SurfaceFlinger 行。在图 11 中,光标的垂直位置由蓝色细线表示。SurfaceFlinger 行浅蓝色背景显示了其水平位置。使用向右键逐个查看过渡,看看是否会出现闪烁。确认设备在进行此过渡时看起来正确无误。 
- 跳过下一个过渡,因为其持续时间很短,不太可能包含闪烁。改为点击 SurfaceFlinger 行内下一个较长过渡的起始位置处的时间轴,如下图中的光标所示。   - 图 12. 第二个过渡结束。 - 在此过渡期间,在 - 15:51:13.239时,请注意两个应用(通讯录和拨号器)的- Splash Screen层位于屏幕的同一侧:  - 图 13. 启动画面。 
- 明确哪个应用在错误的一侧。点击 ns 输入字段旁边的标志图标,为当前位置添加书签,以便日后返回到此帧。   - 图 14. 添加书签。 
- 直接点击时间轴,例如点击 - 15:51:13.859,以跳至过渡结束时的某个帧。此时,这两个应用已位于最终位置,左侧是拨号器,右侧是通讯录:  - 图 15. 最终分屏。 
- 点击时间轴中的书签标志,返回到闪烁的帧。   - 图 16. 书签时间轴。 - 这两个应用都在右侧,这表明拨号器处于错误位置。 
- 点击拨号器的启动画面可查看其属性。请在精选的属性视图中特别注意其转换属性。   - 图 17. 转换属性。 - 计算出的转换会应用于此 surface,但不会设置为此级别。计算的列和请求的列具有不同的值,这表明转换是从父 surface 继承的。 
- 取消选择层次结构视图中的 Flat,以显示整个层次结构树,然后导航到应用 surface 的父级节点,直到 Calculated 和 Requested 转换都相同,并显示 - Surface(name=Task=7934)/@0x1941191_transition-leash#40670surface 上正在请求的转换。
- 确认转换的首次设置时间以及设置的值。点击标题旁边的图标,以收起精选属性:   - 图 18. 收起精选属性。 
- 在 Proto Dump 视图中选择 Show diff,以突出显示在此帧中更改的属性。在文本搜索字段中输入 - transform,以过滤属性:  - 图 19. Show diff。 - 此帧中的 - transition-leash的转换从- IDENTITY改设为- SCALE|TRANSLATE|ROT_270。- 此信息显示,在将转换应用于拨号器分屏应用的动画机制时会发生闪烁。   - 图 20. 确定闪烁。 
- 在代码中确定此转换为何设置为分屏过渡机制。 
