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
可能是所选图层。为了验证这一假设,请选择当前帧上的可见
NotificationShade
surface 并检查其属性。标志设置为OPAQUE|ENABLE_BACKPRESSURE (0x102)
。NotificationShade
surface 名称为NotificationShade#3447
。接下来,按左箭头返回到上一个画面(闪烁之前),然后再次检查NotificationShade
surface 的属性。请注意,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#64
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#40670
surface 上正在请求的转换。确认转换的首次设置时间以及设置的值。点击标题旁边的图标,以收起精选属性:
图 18. 收起精选属性。
在 Proto Dump 视图中选择 Show diff,以突出显示在此帧中更改的属性。在文本搜索字段中输入
transform
,以过滤属性:图 19. Show diff。
此帧中的
transition-leash
的转换从IDENTITY
改设为SCALE|TRANSLATE|ROT_270
。此信息显示,在将转换应用于拨号器分屏应用的动画机制时会发生闪烁。
图 20. 确定闪烁。
在代码中确定此转换为何设置为分屏过渡机制。