使用 Winscope 追蹤視窗轉換

Winscope 是一項網頁工具,可供使用者在動畫和轉場效果期間及之後,記錄、重播及分析多項系統服務的狀態。Winscope 會將所有相關的系統服務狀態記錄到追蹤記錄檔。使用 Winscope UI 和追蹤記錄檔,您可以重播、逐步執行及偵錯轉場效果,檢查每個動畫影格的服務狀態,無論是否錄製螢幕畫面都適用。

支援的追蹤

Winscope 可收集並以視覺化方式呈現各種「追蹤記錄」或系統服務狀態序列。您可以設定這些追蹤記錄,以配合特定用途,範圍從低負荷到高詳細程度。Winscope 支援下列追蹤記錄:

  • EventLog:使用 EventLog 收集系統診斷事件記錄。 在 Winscope 中,這項資訊只會用於識別及顯示 CUJ 標記。
  • 輸入法編輯器:追蹤輸入法編輯器 (IME) 管道的事件,包括 IMS、IMMS 和 IME 用戶端。
  • 輸入:追蹤輸入事件管道各部分的輸入事件。
  • ProtoLog:收集來自系統服務的 ProtoLog 訊息,以及在用戶端程序中執行的系統服務程式碼。
  • 螢幕錄影:收集螢幕錄影畫面和追蹤記錄。
  • 殼層轉場效果:記錄視窗和活動轉場效果的系統詳細資料。
  • SurfaceFlinger:收集包含 Surface (圖層) 相關資訊 (例如位置、緩衝區和組合) 的 SurfaceFlinger 追蹤記錄。
  • 交易:使用 SurfaceControl 追蹤 SurfaceFlinger 收到的原子變更集,以進行組合。
  • ViewCapture:擷取系統 Windows 中所有支援 ViewCapture 的檢視區塊屬性範圍,例如系統 UI 和啟動器。
  • 視窗管理員:追蹤視窗管理員狀態,其中包含與視窗相關的詳細資料,包括輸入和焦點事件、螢幕方向、轉場效果、動畫、定位和轉換。

支援的傾印

Winscope 可以收集及顯示狀態傾印,也就是使用者在特定時間點擷取的裝置狀態快照。與在裝置使用期間持續收集且可能影響效能的追蹤記錄不同,傾印只會在使用者定義的時刻擷取,確保效能和詳細程度不會受到影響。這樣就能在特定時間點更有效率地分析裝置狀態。Winscope 支援下列傾印:

  • 視窗管理員:傾印單一視窗管理員狀態。
  • SurfaceFlinger:傾印單一 SurfaceFlinger 快照。
  • 螢幕截圖:連同傾印檔案一併收集螢幕截圖。

資源

如要瞭解如何建構及執行 Winscope,請參閱「執行 Winscope」。

如要瞭解如何收集追蹤記錄,請參閱「擷取追蹤記錄」。

如要瞭解如何使用 Winscope 網頁版 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 到達網頁,顯示 SurfaceFlinger 檢視畫面

    圖 1. Winscope 到達網頁,顯示 SurfaceFlinger 檢視畫面。

  2. 將例外狀況訊息中的時間戳記複製並貼到時間戳記欄位,即可前往發生問題的時間戳記。您可以複製使用者可解讀格式的時間戳記 (2024-05-10T11:04:14.227572545) 並貼到第一個欄位,也可以複製奈秒格式的時間戳記 (1715339054227572545ns) 並貼到第二個欄位。

    時間戳記對話方塊

    圖 2. 時間戳記對話方塊。

  3. 按向左鍵即可前往上一個影格。在此狀態下,NotificationActivity 應用程式會正確顯示在影片中,且應用程式和啟動畫面表面都會顯示,3D 檢視畫面中的綠色矩形會指出這點,而階層元素上也會顯示 V 晶片。

    應用程式和啟動畫面的介面名稱如下:

    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,而非應用程式。這個影格會顯示下列介面:

    • 螢幕裝飾疊加層 (頂端和底部)
    • 導覽列
    • 指標位置 (來自螢幕錄影)

      Flicker 活動

      圖 4. 閃爍活動。

  5. 在階層檢視中選取應用程式活動。如果找不到,請取消勾選「僅顯示 V」,然後檢查屬性檢視畫面。

    應用程式介面名稱為:

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

    應用程式屬性

    圖 5. 應用程式屬性。

    雖然應用程式活動設為可見且不透明,但由於發生 Invisible due to: null visible region 錯誤,因此系統不會顯示介面。這是因為在組合期間,另一個不透明的表面放置在該表面前方。這項假設的依據是 3D 檢視畫面中的 NotificationShade 矩形位於 NotificationActivity 矩形前方,且可見 (綠色) 的 NotificationShade 可能是所選圖層。

  6. 如要驗證這項假設,請選取目前影格中可見的 NotificationShade 表面,然後檢查其屬性。旗標已設為 OPAQUE|ENABLE_BACKPRESSURE (0x102)NotificationShade 介面名稱為 NotificationShade#3447。接著,按下向左箭頭,返回上一個影格 (閃爍前),然後再次檢查 NotificationShade 表面屬性。請注意,介面只有 ENABLE_BACKPRESSURE (0x100) 旗標,而非 OPAQUE。這會確認 NotificationShade 在應用程式啟動完全完成前會變成不透明。由於 NotificationShade 位於 NotificationActivity 前方,因此應用程式不會顯示。NotificationShade 為黑色,因此螢幕會短暫變黑,導致閃爍。

  7. 找出程式碼中 NotificationShade 過早變成不透明的原因。

使用者回報的錯誤

使用者回報的錯誤通常缺乏詳細資訊,因此很難進行偵錯。與閃爍測試失敗不同,使用者回報的錯誤通常只包含問題的簡短說明,而閃爍測試失敗則會提供具體的時間戳記、元素詳細資料和螢幕錄影。

在本案例研究中,我們只提供「從分割畫面重新開啟應用程式時,螢幕會閃爍」這個標題,以及「2024 年 4 月 18 日下午 3:51 (GMT-04:00)」這個大約的時間戳記。

如要偵錯使用者回報的錯誤,請按照下列步驟操作:

  1. 在 Winscope 中載入追蹤記錄檔案。Winscope 會開啟,並自動選取 SurfaceFlinger。

    Winscope 到達網頁,顯示 SurfaceFlinger 檢視畫面

    圖 6. Winscope 到達網頁,顯示 SurfaceFlinger 檢視畫面。

  2. 在可讀的時間戳記欄位中輸入 15:50:00,即可前往使用者回報的大約時間戳記,在本例中為 3:50 PM GMT-04:00

    時間戳記對話方塊

    圖 7. 時間戳記對話方塊。

  3. 使用矩形檢視畫面,找出螢幕上繪製的內容。如要調整矩形視角,請使用「旋轉」滑桿,在「階層」檢視畫面中標示「僅顯示 V」和「平面」,即可查看桌布、螢幕裝飾疊加層、上下黑邊、啟動器、聯絡人和撥號程式介面。

    套件名稱如下:

    • 啟動器: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

    除了可見的介面 (綠色矩形) 之外,系統還會顯示代表螢幕區域介面的灰色矩形,並命名為「Unknown display」(不明螢幕)。如要提高能見度,請按一下 ScreenDecorHwcOverlay#64 表面旁的 (顯示設定圖示),隱藏對應的矩形,並顯示後方的表面。我們會移除分析的疊加層,因為使用者看不到,也不會回報為閃爍動畫。

    使用者回報

    圖 8. 使用者回報。

  4. 找出參與分割畫面檢視的介面後,請使用 Transitions 追蹤記錄逐步執行各種使用者動作,找出閃爍問題。在 Winscope 中按一下「Transitions」分頁,即可查看播放的轉場效果清單:

    轉場效果

    圖 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.239Splash Screen

    啟動畫面

    圖 13. 啟動畫面。

  8. 請說明哪一個應用程式顯示在錯誤的一側。按一下 ns 輸入欄位旁的旗幟圖示,即可在目前位置新增書籤,方便日後返回這個影格。

    新增書籤

    圖 14. 新增書籤。

  9. 直接點選時間軸,例如 15:51:13.859,即可前往轉場效果結尾的影格。現在這兩個應用程式已就位,左側是撥號程式,右側是聯絡人:

    最終分割畫面

    圖 15. 最終分割畫面。

  10. 按一下時間軸中的書籤旗標,返回閃爍的影格。

    書籤時間軸

    圖 16. 為時間軸加上書籤。

    兩個應用程式都位於右側,表示撥號程式的位置不正確。

  11. 按一下撥號器的啟動畫面,即可查看其屬性。請特別查看精選「屬性」檢視畫面中的轉換屬性。

    轉換屬性

    圖 17. 轉換屬性。

    計算出的轉換會套用至這個介面,但不會設為這個層級。「計算」和「要求」欄的值不同,表示轉換是從上層介面沿用。

  12. 在階層檢視中取消選取「Flat」,顯示整個階層樹狀結構,然後前往應用程式介面的父節點,直到「Calculated」和「Requested」轉換相同為止,顯示在 Surface(name=Task=7934)/@0x1941191_transition-leash#40670 介面上要求的轉換。

  13. 確認轉換的首次設定時間和值。按一下標題旁的圖示,即可收合精選屬性:

    收合精選屬性

    圖 18. 收合精選屬性。

  14. 在「Proto Dump」檢視畫面中選取「Show diff」,即可醒目顯示這個影格中變更的屬性。在文字搜尋欄位中輸入 transform,即可篩選屬性:

    顯示差異

    圖 19. 顯示差異。

    在此影格中,轉換會從 IDENTITY 設為 SCALE|TRANSLATE|ROT_270transition-leash

    這項資訊顯示,當轉換套用至撥號程式分割畫面應用程式的動畫牽繩時,發生了閃爍情形。

    辨識閃爍

    圖 20. 閃爍的識別資訊。

  15. 在程式碼中找出這個轉換設定為分割畫面轉場動畫的原因。