使用 Winscope 跟踪窗口转换

Winscope 是一款 Web 工具,用户可以使用它来录制、重放和分析动画和过渡期间及之后多个系统服务的状态。Winscope 会将所有相关的系统服务状态记录到一个跟踪文件。通过将 Winscope 界面与跟踪文件结合使用,您可以重放、逐步浏览和调试过渡,从而检查每个动画帧的这些服务的状态(无论是否带有屏幕录制内容)。

支持的跟踪记录

Winscope 能够收集和直观地呈现各种跟踪记录或系统服务状态序列。您可以配置这些跟踪记录以适应特定的用例,范围从低开销到高详细程度。Winscope 支持以下跟踪记录

  • EventLog:使用 EventLog 收集系统诊断事件记录。在 Winscope 中,此信息仅用于识别和显示 CUJ 标记。
  • IME:跟踪来自输入法编辑器 (IME) 管道的事件,包括 IMS、IMMS 和 IME 客户端。
  • 输入:跟踪来自输入事件管道各个部分的输入事件。
  • ProtoLog:收集来自系统服务和在客户端进程中运行的系统服务代码的 ProtoLog 消息。
  • 屏幕录制:在跟踪记录旁边收集屏幕录制内容。
  • Shell 过渡:记录窗口和 Activity 过渡系统详细信息。
  • SurfaceFlinger:收集 SurfaceFlinger 跟踪记录,其中包含有关表面(图层)的信息,例如位置、缓冲区和合成。
  • 事务 (Transactions): 使用 SurfaceControl 跟踪 SurfaceFlinger 接收到的一组原子更改,以进行合成。
  • ViewCapture: 捕获来自支持 ViewCapture 的系统窗口(如系统 UI 和启动器)的所有视图的一系列属性。
  • 窗口管理器 (Window Manager): 跟踪 窗口管理器 状态,其中包含与窗口相关的详细信息,包括输入和焦点事件、屏幕方向、过渡、动画、定位和变换。

支持的转储

Winscope 可以收集和显示状态转储,这些转储是在用户定义的特定时刻拍摄的设备状态快照。与在设备使用期间持续收集并可能影响性能的跟踪记录不同,转储仅在这些用户定义的时刻拍摄,从而确保性能和详细程度不受影响。这样可以更集中和高效地分析设备在特定时间点的状态。Winscope 支持以下转储

  • 窗口管理器 (Window Manager): 转储单个窗口管理器状态。
  • SurfaceFlinger: 转储单个 SurfaceFlinger 快照。
  • 屏幕截图 (Screenshot): 在转储的同时收集屏幕截图。

资源

请参阅运行 Winscope,了解有关构建和运行 Winscope 的信息。

请参阅捕获跟踪记录,了解有关收集跟踪记录的信息。

请参阅加载跟踪记录,了解如何使用 Winscope Web UI 加载跟踪记录的信息。

请参阅分析跟踪记录,了解有关分析跟踪记录的信息。

示例

以下示例描述了如何调试闪烁测试失败和用户报告的错误。

闪烁测试失败

此示例演示了如何使用 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

    Winscope landing page with SurfaceFlinger view

    图 1. 带有 SurfaceFlinger 视图的 Winscope 登录页。

  2. 通过将异常消息中的时戳复制并粘贴到时戳字段中,导航到问题发生时的时戳。您可以复制人类可读格式的时戳 (2024-05-10T11:04:14.227572545) 并粘贴到第一个字段,或者复制纳秒为单位的时戳 (1715339054227572545ns) 并粘贴到第二个字段。

    Timestamp dialog

    图 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
    

    这表明当屏幕变黑时应用正在启动,并且此事件发生在应用启动期间,因为启动画面仍然可见

    At app launch

    图 3. 应用启动时。

  4. 按向右箭头键导航回下一帧,闪烁发生在这一帧。在矩形视图中,屏幕上显示的是 NotificationShade,而不是应用。此帧中显示了以下 Surface

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

      Flicker activity

      图 4. 闪烁活动。

  5. 在层级视图中选择应用活动。如果找不到它,请取消选中仅显示 V。然后,检查属性视图。

    应用 Surface 名称为

    com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.NotificationActivity#3458`
    

    App properties

    图 5. 应用属性。

    尽管应用活动设置为可见且不透明,但由于 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 不再是 OPAQUE,而仅具有 ENABLE_BACKPRESSURE (0x100) 标志。这证实了 NotificationShade 在应用启动完全完成之前变得不透明。NotificationShade 位于 NotificationActivity 的前面,因此应用未显示。NotificationShade 是黑色的,因此屏幕短暂变黑,从而导致闪烁。

  7. 在代码中确定 NotificationShade 为何过早变得不透明。

用户报告的错误

用户报告的错误可能难以调试,因为它们通常缺乏详细信息。与闪烁测试失败(提供具体的时戳、元素详细信息和屏幕录制)不同,用户报告的错误通常仅包含对问题的简要描述。

在我们的案例研究中,提供的唯一信息是标题从分屏重新打开应用时屏幕闪烁以及大约时戳2024 年 4 月 18 日下午 3:51 GMT-04:00

按照以下步骤调试用户报告的错误

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

    Winscope landing page with SurfaceFlinger view

    图 6. 带有 SurfaceFlinger 视图的 Winscope 登录页。

  2. 导航到用户报告的大约时戳,在本例中为 3:50 PM GMT-04:00,方法是在人类可读的时戳字段中输入 15:50:00

    Timestamp dialog

    图 7. 时戳对话框。

  3. 使用矩形视图来识别屏幕上绘制的内容。为了获得更好的视图,请使用旋转滑块来更改矩形的透视。通过在层级视图中标记仅显示 V平面,壁纸、屏幕装饰叠加层、信箱、启动器、联系人和拨号器 Surface 可见。

    软件包名称为

    • 启动器 (Launcher):com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#40602

    • 联系人 (Contacts):com.google.android.contacts/com.android.contacts.activities.PeopleActivity#40565

    • 拨号器 (Dialer):com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity#40564

    除了可见的 Surface(绿色矩形)之外,还显示了一个灰色矩形,它代表显示区域 Surface,名为未知显示。为了提高可见性,请点击 (visibility icon) ScreenDecorHwcOverlay#64 Surface 旁边的图标以隐藏其对应的矩形并显示后面的 Surface。我们删除叠加层以进行分析,因为它对用户不可见,并且不会被报告为闪烁动画。

    User report

    图 8. 用户报告。

  4. 在您确定了哪些 Surface 参与分屏视图后,使用“过渡 (Transitions)”跟踪记录逐步浏览各种用户操作并找到闪烁。点击 Winscope 中的过渡 (Transitions) 标签页以可视化播放的过渡列表

    transitions

    图 9. 过渡 (Transitions)。

    在此帧期间播放的过渡以蓝色突出显示。在本例中,过渡标志包括 TRANSIT_FLAG_IS_RECENTS,这表示用户正在进入最近任务屏幕。

  5. 点击调度时间 (Dispatch Time) 列上的链接(在本例中为 2024-04-18, 15:50:57.205)以导航到该时间点并在 Surface Flinger 标签页中验证矩形。通过使用向右箭头键逐步浏览过渡并观察矩形,确认设备在过渡期间的状态正确性。

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

    Screen before flicker

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

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

    End of first transition

    图 11. 第一个过渡的结束。

    在下一个过渡的起始位置点击 SurfaceFlinger 行。在图 11 中,光标的垂直位置由细蓝线指示。SurfaceFlinger 行的浅蓝色背景显示其水平位置。使用向右箭头键逐步浏览过渡,以查看是否发生闪烁。确认设备在此过渡期间看起来是否正确。

  7. 跳过下一个过渡,因为它的持续时间非常短,因此不太可能包含闪烁。相反,在下一个较长过渡的起始位置点击 SurfaceFlinger 行中的时间轴,如图中光标所示。

    end of second transition

    图 12. 第二个过渡的结束。

    在此过渡期间,在 15:51:13.239,观察到两个应用的 Splash Screen 层、联系人和拨号器都在显示屏的同一侧

    splash screens

    图 13. 启动画面 (Splash screens)。

  8. 澄清哪个应用在错误的一侧。通过点击 ns 输入字段旁边的标志图标,为当前位置添加书签,以帮助稍后导航回此帧。

    add bookmark

    图 14. 添加书签。

  9. 导航到过渡结束时的帧,例如,15:51:13.859。在这里,这两个应用现在处于最终位置,拨号器在左侧,联系人在右侧

    final split screen

    图 15. 最终分屏。

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

    bookmark timeline

    图 16. 书签时间轴。

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

  11. 点击拨号器的启动画面以查看其属性。特别查看精选的属性 (Properties) 视图中的变换属性。

    Transform properties

    图 17. 变换属性。

    计算出的变换应用于此 Surface,但未在此级别设置。计算列和请求列具有不同的值,表明变换是从父 Surface 继承的。

  12. 在层级视图中取消选择平面以显示整个层级树,并导航到应用 Surface 的父节点,直到计算请求变换相同,显示在 Surface(name=Task=7934)/@0x1941191_transition-leash#40670 Surface 上请求的变换。

  13. 确认变换首次设置的时间以及设置为哪个值。点击标题旁边的图标折叠精选属性

    collapse the curated properties

    图 18. 折叠精选属性。

  14. Proto Dump 视图中选择显示差异 (Show diff) 以突出显示在此帧中正在更改的属性。在文本搜索字段中键入 transform 以过滤属性

    show diff

    图 19. 显示差异 (Show diff)。

    在此帧中,transition-leash 的变换从 IDENTITY 设置为 SCALE|TRANSLATE|ROT_270

    此信息表明,当变换应用于拨号器分屏应用的动画 leash 时,发生闪烁。

    Identification of the flicker

    图 20. 闪烁的识别。

  15. 在代码中确定为何此变换设置为分屏过渡 leash。