使用 Winscope 跟踪窗口转换

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 调试闪烁测试失败问题。

检查测试失败情况

请按照以下步骤确定问题类型并检查测试失败消息。

  1. 通过检查测试和类名称来确定问题类型。

    测试和类名称:

    FlickerTestsNotification com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest#appLayerBecomesVisible[ROTATION_0_GESTURAL_NAV]
    

    问题类型:

    • CUJ 是指通过锁屏通知启动应用 (OpenAppFromLockscreenNotificationColdTest)。

    • 测试预计应用会变为可见 (#appLayerBecomesVisible)。

  2. 检查测试失败消息,其中包含有关失败的全面信息,包括:

    • 预期结果与实际可见结果之间的比较
    • 时间戳,可帮助您查明发生失败问题的时间
    • 与失败相关的制品或文件的名称
    • 与理解和调试失败问题相关的其他背景信息
    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

调试

请按以下步骤确定闪烁的原因:

  1. 下载跟踪记录文件,并在 Winscope 中加载这些文件。Winscope 会打开,并自动选择 SurfaceFlinger:

    包含 SurfaceFlinger 视图的 Winscope 着陆页

    图 1. 包含 SurfaceFlinger 视图的 Winscope 着陆页。

  2. 将异常消息中的时间戳复制并粘贴到时间戳字段中,以找到问题发生时的时间戳。您可以复制人类可读格式 (2024-05-10T11:04:14.227572545) 的时间戳并粘贴到第一个字段,也可以复制纳秒级时间戳 (1715339054227572545ns) 并粘贴到第二个字段。

    时间戳对话框

    图 2. 时间戳对话框。

  3. 按向左键可前往上一个帧。在此状态下,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. 在应用启动时。

  4. 按向右键可返回到下一个闪烁的帧。在矩形视图中,屏幕上显示的是 NotificationShade,而不是应用。此帧中显示以下 surface:

    • 屏幕装饰叠加层(顶部和底部)
    • 导航栏
    • 指针位置(来自屏幕录制)

      闪烁 activity

      图 4. 闪烁 activity。

  5. 在层次结构视图中选择应用 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 可能是所选图层。

  6. 为了验证这一假设,请选择当前帧上的可见 NotificationShade surface 并检查其属性。标志设置为 OPAQUE|ENABLE_BACKPRESSURE (0x102)NotificationShade surface 名称为 NotificationShade#3447。接下来,按左箭头返回到上一个画面(闪烁之前),然后再次检查 NotificationShade surface 的属性。请注意,surface 仅具有 ENABLE_BACKPRESSURE (0x100) 标志,而不是 OPAQUE。这可以确认 NotificationShade 在应用启动完全结束之前变为不透明。由于 NotificationShade 位于 NotificationActivity 前面,因此应用不会显示。NotificationShade 为黑色,因此屏幕会短暂变黑,从而导致闪烁。

  7. 在代码中找出 NotificationShade 过早变暗的原因。

用户报告的 bug

用户报告的 bug 通常缺少详细信息,因此很难进行调试。不同于闪烁测试失败(提供特定时间戳、元素详情和屏幕录制内容),用户报告的 bug 通常只包含问题的简要说明。

在我们的案例研究中,唯一提供的信息是标题“Screen flickered when reopening app from split screen” 和大致时间戳“Apr 18, 2024 3:51 PM GMT-04:00”

请按以下步骤对用户报告的 bug 进行调试:

  1. 在 Winscope 中加载跟踪文件。Winscope 打开时会自动选择 SurfaceFlinger。

    包含 SurfaceFlinger 视图的 Winscope 着陆页

    图 6. 包含 SurfaceFlinger 视图的 Winscope 着陆页。

  2. 在人类可读的时间戳字段中输入 15:50:00,以找到用户报告的大致时间戳(在本例中为 3:50 PM GMT-04:00)。

    时间戳对话框

    图 7. 时间戳对话框。

  3. 使用矩形视图可识别屏幕上绘制的内容。如需获得更好的视图,请使用“旋转”滑块更改矩形视角。在层次结构视图中标记 Show only VFlat 后,壁纸、屏幕装饰叠加层、信箱、启动器、联系人和拨号器 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#64 surface 旁边的 (可见性图标) 以隐藏其对应的矩形,并显示后面的 surface。我们移除了分析的叠加层,因为它对用户不可见,也不会被报告为闪烁动画。

    用户报告

    图 8. 用户报告。

  4. 确定分屏视图中涉及哪些 surface 后,请使用“过渡”跟踪记录来逐步执行各种用户操作并查找闪烁。点击 Winscope 中的过渡标签页,以直观呈现已播放的过渡的列表:

    过渡

    图 9. 过渡。

    在此帧中播放的过渡以蓝色突出显示。在本例中,过渡标志包括 TRANSIT_FLAG_IS_RECENTS,表示用户正在进入最近使用的界面。

  5. 点击 Dispatch Time 列(在本例中为 2024-04-18, 15:50:57.205)上的链接,前往相应时间点,然后在 Surface Flinger 标签页中验证矩形。通过使用向右箭头键逐步过渡并观察矩形,确认设备在过渡期间的状态是否正确。

    启动器在 15:50:57.278 时出现,但动画不会在该时间开始。壁纸已经可见,因为在分屏应用(分隔线)之间没有绘制任何内容。在前一个帧 (15:50:57.212) 中,壁纸不可见,并且显示了分隔线,这是未进行动画处理时分屏的显示效果。

    闪烁事件发生前的屏幕

    图 10. 闪烁事件发生前的屏幕。

  6. 如需查看下一个过渡,请直接点击时间轴。SurfaceFlinger 状态由一行浅蓝色块表示。过渡由一行粉色块表示。

    第一个过渡结束

    图 11. 第一个过渡结束。

    点击下一个过渡的起始位置处的 SurfaceFlinger 行。在图 11 中,光标的垂直位置由蓝色细线表示。SurfaceFlinger 行浅蓝色背景显示了其水平位置。使用向右键逐个查看过渡,看看是否会出现闪烁。确认设备在进行此过渡时看起来正确无误。

  7. 跳过下一个过渡,因为其持续时间很短,不太可能包含闪烁。改为点击 SurfaceFlinger 行内下一个较长过渡的起始位置处的时间轴,如下图中的光标所示。

    第二个过渡结束

    图 12. 第二个过渡结束。

    在此过渡期间,在 15:51:13.239 时,请注意两个应用(通讯录和拨号器)的 Splash Screen 层位于屏幕的同一侧:

    启动画面

    图 13. 启动画面。

  8. 明确哪个应用在错误的一侧。点击 ns 输入字段旁边的标志图标,为当前位置添加书签,以便日后返回到此帧。

    添加书签

    图 14. 添加书签。

  9. 直接点击时间轴,例如点击 15:51:13.859,以跳至过渡结束时的某个帧。此时,这两个应用已位于最终位置,左侧是拨号器,右侧是通讯录:

    最终分屏

    图 15. 最终分屏。

  10. 点击时间轴中的书签标志,返回到闪烁的帧。

    书签时间轴

    图 16. 书签时间轴。

    这两个应用都在右侧,这表明拨号器处于错误位置。

  11. 点击拨号器的启动画面可查看其属性。请在精选的属性视图中特别注意其转换属性。

    转换属性

    图 17. 转换属性。

    计算出的转换会应用于此 surface,但不会设置为此级别。计算的列和请求的列具有不同的值,这表明转换是从父 surface 继承的。

  12. 取消选择层次结构视图中的 Flat,以显示整个层次结构树,然后导航到应用 surface 的父级节点,直到 CalculatedRequested 转换都相同,并显示 Surface(name=Task=7934)/@0x1941191_transition-leash#40670 surface 上正在请求的转换。

  13. 确认转换的首次设置时间以及设置的值。点击标题旁边的图标,以收起精选属性:

    收起精选属性

    图 18. 收起精选属性。

  14. Proto Dump 视图中选择 Show diff,以突出显示在此帧中更改的属性。在文本搜索字段中输入 transform,以过滤属性:

    Show diff

    图 19. Show diff。

    此帧中的 transition-leash 的转换从 IDENTITY 改设为 SCALE|TRANSLATE|ROT_270

    此信息显示,在将转换应用于拨号器分屏应用的动画机制时会发生闪烁。

    确定闪烁

    图 20. 确定闪烁。

  15. 在代码中确定此转换为何设置为分屏过渡机制。