Android UI性能測試——使用 Systrace 查找問題

一 官方文檔翻譯html

官文地址:https://developer.android.com/studio/command-line/systracepython

systrace命令容許您在系統級別上收集和檢查全部運行在設備上的進程的耗時信息。它結合了來自Android內核的數據,例如CPU調度程序,磁盤活動和app線程,最後生成HTML報告,相似於圖1中所示。android

圖1:systrace HTML示例報告,程序默認抓取5秒內應用和系統的消耗。該報告突出顯示了systrace認爲的異常幀。web

此報告提供了在給定時間段內Android設備的系統進程的整體狀況。它還檢查捕獲的跟蹤信息以突出顯示它所發現的問題,例如在顯示動做或動畫時的UI jank,並提供有關如何修復它們的建議。可是, systrace工具不會收集有關app進程中代碼執行的信息。有關app正在執行的方法以及它使用的CPU資源的更多詳細信息,請使用Android Studio自帶的的CPU profiler,您還能夠生成跟蹤日誌並使用CPU profiler導入和檢查它們。json

本文檔說明如何從命令行經過systrace生成報告,查看生成的跟蹤文件,並使用它們分析和改進app用戶界面(UI)的性能。bootstrap

注意:在運行Android 9(API級別28)或更高版本的設備上,您還可使用名爲「系統跟蹤」的系統應用程序來記錄設備上的系統跟蹤。瀏覽器

要運行systrace,請完成如下步驟app

1.從Android Studio下載並安裝最新的Android SDK工具。
2.安裝Python並配置環境變量。
3.使用USB調試將運行Android 4.3(API級別18)或更高版本的設備鏈接到開發系統。ionic

Systrace是Android SDK中自帶的一個命令工具,位於android-sdk/platform-tools/systrace/ide

語法

要爲app生成HTML報告,您須要使用systrace的如下語法從命令行運行:

$ python systrace.py [options] [categories]

例如,如下命令調用systrace記錄設備活動並生成名爲mynewtrace.html的HTML報告。對於大多數設備來講,此類別列表是合理的默認列表。

$ python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
binder_driver hal dalvik camera input res

提示:若是要在跟蹤輸出中查看任務的名稱,則必須在命令參數中包含sched。

要查看已鏈接設備支持的類別列表,請運行如下命令:

$ python systrace.py --list-categories

若是未指定任何類別或選項,systrace默認會生成包含全部可用類別並使用默認設置的報告。可用的類別取決於您鏈接的設備。

全局選項

命令和命令選項

調查UI性能問題

systrace對於檢測app的UI性能特別有用,由於它能夠分析您的代碼和幀率,以識別問題所在並提出可能的解決方案或建議。首先,按如下步驟操做:

1.在鏈接的設備上運行您的應用。

2.systrace使用如下命令運行(此命令會跟蹤您的應用10秒鐘)

$ python systrace.py -t 10 [other-options] [categories]

3.在systrace運行的同時與您的應用互動。

4.在您定義的時間限制結束後(本次示例是10秒),systrace生成HTML報告。

5.使用Web瀏覽器打開HTML報告。

經過此報告,您能夠檢測設備CPU在記錄期間的使用狀況。

如下部分介紹瞭如何查閱報告中的信息以發現和修復UI性能問題。

查看幀和警告

如圖2所示,該報告列出了每一個進程沿時間軸的每一個渲染幀。綠色幀表示在16.6毫秒內渲染並保持每秒60幀穩定幀率的幀,黃色或紅色幀表渲染時間超過16.6毫秒的幀。

圖2:Systrace報告顯示放大耗時長的幀。

注意:在運行Android 5.0(API級別21)或更高版本的設備上,渲染幀的工做分配給了UI Thread和RenderThread。在之前的版本中,建立幀的全部工做都被UI Thread完成。

單擊幀會突出顯示它,並顯示有關係統完成該幀所作工做的其餘信息,包括警告。它還會向您顯示系統在渲染該幀時執行的方法,所以您能夠調查這些方法以獲取UI jank的緣由。

圖3.單擊異常幀,跟蹤報告下方會出現一個警告,用於識別問題。

選擇慢速幀後,您會在報告的底部窗格中看到警告。圖3中顯示的警告指出此幀的主要問題是在ListView回收和從新綁定中花費了太多時間 。點擊跟蹤中相關事件的連接,能夠更詳細地查看系統在此期間所執行的操做。

要查看Systrace工具在跟蹤中發現的每一個警告,以及設備觸發每一個警告的次數,請單擊窗口最右側的Alert按鈕,如圖4所示。警告面板可幫助您查看在跟蹤中發生的問題,以及它們對jank的貢獻頻率。能夠將警告面板視爲要修復的錯誤列表。一般一個區域的微小變化或改進能夠消除應用程序中的整個警告類別。

圖4:單擊右側的Alert按鈕顯示警告面板。

若是你發現UI線程作了不少的工做,你須要找出具體是哪些方法消耗了大多數CPU時間。一種方法是將跟蹤標記添加到您認爲致使這些瓶頸的方法中,而後在systrace中查看這些函數調用。若是您不肯定哪些方法可能致使UI線程出現瓶頸,請使用Android Studio的CPU Profiler。您可使用CPU Profiler生成跟蹤日誌並導入和檢查它們。

HTML報告的鍵盤快捷鍵

下表列出了查看systrace HTML報告時可用的鍵盤快捷鍵。

檢查應用代碼

由於systrace僅在系統級別顯示有關進程的信息,因此在HTML報告中很難了解你的APP在給定時間內具體執行的方法。在Android 4.3(API級別18)及更高版本中,您可使用代中的Trace類來標記HTML報告中的執行事件,您不須要經過檢查代碼來記錄跟蹤 。可是檢查代碼能夠幫助您瞭解app代碼的哪些部分可能會致使線程掛起或UI jank。這種方法與使用Debug類不一樣——Trace類只是向systrace報告添加標記,而Debug 類是經過生成.trace文件來幫助您詳細檢查app的CPU使用狀況。

要生成包含已檢測跟蹤事件的systrace HTML報告,您須要使用systrace的-a或--app選項運行命令行並指定app的包名稱。

下面的示例代碼演示如何使用Trace類標記一個方法的執行,包括該方法中的兩個嵌套代碼塊:

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    ...
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Trace.beginSection(&quot;MyAdapter.onCreateViewHolder&quot;);
        MyViewHolder myViewHolder;
        try {
            myViewHolder = MyViewHolder.newInstance(parent);
        } finally {
            // In &#39;try...catch&#39; statements, always call <code><a href="/reference/android/os/Trace.html#endSection()">endSection()</a></code>
            // in a &#39;finally&#39; block to ensure it is invoked even when an exception
            // is thrown.
            Trace.endSection();
        }
        return myViewHolder;
    }</p>

<p>@Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        Trace.beginSection(&quot;MyAdapter.onBindViewHolder&quot;);
        try {
            try {
                Trace.beginSection(&quot;MyAdapter.queryDatabase&quot;);
                RowItem rowItem = queryDatabase(position);
                mDataset.add(rowItem);
            } finally {
                Trace.endSection();
            }
            holder.bind(mDataset.get(position));
        } finally {
            Trace.endSection();
        }
    }
...
}

注意:當您屢次調用beginSection(String)方法時,每次會調用離beginSection(String)方法最近的endSection()方法。所以,對於嵌套調用,例如上面示例中的調用,您須要確保將每一個beginSection()方法的調用都正確匹配一個endSection()方法的調用。此外,在一個線程中你不能只調用beginSection()方法,您必須在同一線程中調用endSection()方法來結束它。

二 補充(摘自《大話APP測試2.0》)

Systrace是相似於Memory Monitor的一個縮小問題訪問、初步判斷問題的工具。但它也不是萬能的,須要TraceView的配合才能最終定位問題。
實戰:假設咱們如今面對着一個比較卡的列表界面,應該按照以下的步驟來執行。
1)保證當前環境下測試的應用版本都是正確和穩定的。
2)在終端開啓Systrace監聽命令(通常開10s就足夠了)。
3)在終端顯示當前正在蒐集Systrace時開始進行你認爲卡頓的那些操做,好比滾動ListView、切換Tab等。
4)看到終端顯示capture trace時意味着Systrace蒐集trace已經結束,此時等待html報告生成便可。
打開Systrace報告,若是看到SurfaceFlinger這一項的色條間隔很大而且很不規則,這說明咱們真實滾動列表時掉幀很厲害,從這個數據上面咱們能夠看出該ListView的功能體驗很很差,而且可以計算出掉幀率。
知道掉幀率以及的確卡頓後,咱們能夠經過「W」、「A」、「S」、「D」把結果放大,而後查看在掉幀的時間段內應用到底作了什麼,這樣就可以大概定位問題出在什麼地方了。
咱們來總結一下,經過Systrace獲得的信息有:
1)應用是否真的卡頓。
2)掉幀率是多少。
3)掉幀時應用到底在幹什麼。
4)單擊Systrace報告中的警告或者錯誤的標籤,看到對應的修改建議。
5)Kernel層CPU的使用狀況。
……
Systrace也並非全部的模塊或功能都支持的,那麼對於一些不支持但又要進行測試的功能應該怎麼辦?在Android4.3或者更高的版本中Android自己提供了Systrace類,能夠進行相關的引用從而最終可以在報告中查看到。以下是官方文檔中所提供的一段代碼,你們能夠參考。

public void ProcessPeople(){
    
    Trace.beginSection("ProcessPeople");        
    try{            
        Trace.beginSection("Processing Jane");            
        try {                
            //code for Jane task...            
        }finish{                
            Trace.endSection();//ends "Processing Jane"            
        }                        

        Trace.beginSection("Processing John");                
        try {                
            //code for John task...            
        }finish{                
            Trace.endSection();//ends "Processing John"            
        }        
    }finish{            
        Trace.endSection();
        //ends "ProcessPeople"        
    }    

}

三 個人實戰

C:\Users\Administrator>adb devices # 查看設備
List of devices attached
GSL7N16B10000581        device

C:\Users\Administrator>D: # 進入D盤

D:\>cd software\Android\SDK\platform-tools\systrace # 進入Systrace目錄

D:\software\Android\SDK\platform-tools\systrace>dir # 查看該目錄:可看到有systrace.py文件
 驅動器 D 中的卷是 軟件
 卷的序列號是 A80B-3BC5

 D:\software\Android\SDK\platform-tools\systrace 的目錄

2018\11\30 週五  14:25    <DIR>          .
2018\11\30 週五  14:25    <DIR>          ..
2018\11\22 週四  10:32    <DIR>          catapult
2018\11\22 週四  10:32            11,738 NOTICE
2018\11\22 週四  10:32             1,456 systrace.py
2018\11\22 週四  10:32                41 UPSTREAM_REVISION
               3 個文件         13,235 字節
               3 個目錄 28,734,406,656 可用字節


D:\software\Android\SDK\platform-tools\systrace>python systrace.py -l #查看設備支持的類別
         gfx - Graphics
       input - Input
        view - View System
     webview - WebView
          wm - Window Manager
          am - Activity Manager
          sm - Sync Manager
       audio - Audio
       video - Video
      camera - Camera
         hal - Hardware Modules
         app - Application
         res - Resource Loading
      dalvik - Dalvik VM
          rs - RenderScript
      bionic - Bionic C Library
       power - Power Management
          pm - Package Manager
          ss - System Server
    database - Database
     network - Network
         adb - ADB
       sched - CPU Scheduling
        freq - CPU Frequency
        idle - CPU Idle
        load - CPU Load
  memreclaim - Kernel Memory Reclaim
  binder_driver - Binder Kernel driver
  binder_lock - Binder global lock trace

NOTE: more categories may be available with adb root



D:\software\Android\SDK\platform-tools\systrace>python systrace.py -o mynewtrace.html -t 10 sched freq idle am wm gfx view binder_driver hal dalvik camera input res #在honor 8 Android 8.0設備上運行,報以下錯誤

Exception in thread Thread-11:
Traceback (most recent call last):
  File "D:\software\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "D:\software\Python27\lib\threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 196, in _collect_and_preprocess
    trace_data = self._collect_trace_data()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 256, in _collect_trace_data
    result = self._stop_collect_trace()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 243, in _stop_collect_trace
    large_output=True, check_return=True, timeout=ADB_LARGE_OUTPUT_TIMEOUT)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\decorators.py", line 57, in timeout_retry_wrapper
    retry_if_func=retry_if_func)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\timeout_retry.py", line 157, in Run
    error_log_func=error_log_func)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\reraiser_thread.py", line 186, in JoinAll
    self._JoinAll(watcher, timeout)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\reraiser_thread.py", line 151, in _JoinAll
    (len(alive_threads), len(self._threads)))
CommandTimeoutError: Timed out waiting for 1 of 1 threads.

Outputting Systrace results...
Tracing complete, writing results
Traceback (most recent call last):
  File "systrace.py", line 49, in <module>
    sys.exit(run_systrace.main())
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\run_systrace.py", line 201, in main
    main_impl(sys.argv)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\run_systrace.py", line 198, in main_impl
    controller.OutputSystraceResults(write_json=options.write_json)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\systrace_runner.py", line 69, in OutputSystraceResults
    self._out_filename)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\output_generator.py", line 99, in GenerateHTMLOutput
    html_file.write(_ConvertToHtmlString(result.raw_data))
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\output_generator.py", line 121, in _ConvertToHtmlString
    raise ValueError('Invalid trace result format for HTML output')
ValueError: Invalid trace result format for HTML output

D:\software\Android\SDK\platform-tools\systrace>python systrace.py -o mynewtrace.html -t 10 sched freq idle am wm gfx view hal dalvik camera input res # 在huawei M2-803L android 5.0.1 EMUI 3.1設備上運行報以下錯誤
Starting tracing (10 seconds)
Tracing completed. Collecting output...
CRITICAL:root:(TimeoutThread-1-for-Thread-11) Exception on ReadFile(YVF6R1741500
0417, /sys/kernel/debug/tracing/tracing_on, retries=3, timeout=30), attempt 1 of
 4: AdbCommandFailedError("(device: YVF6R17415000417) adb pull /sys/kernel/debug
/tracing/tracing_on 'c:\\users\\admini~1\\appdata\\local\\temp\\tmppdnptu\\tmp_R
eadFileWithPull': failed with exit status 1 and output:\n- adb: error: remote ob
ject '/sys/kernel/debug/tracing/tracing_on' does not exist\n",)
CRITICAL:root:(TimeoutThread-2-for-Thread-11) Exception on ReadFile(YVF6R1741500
0417, /sys/kernel/debug/tracing/tracing_on, retries=3, timeout=30), attempt 2 of
 4: AdbCommandFailedError("(device: YVF6R17415000417) adb pull /sys/kernel/debug
/tracing/tracing_on 'c:\\users\\admini~1\\appdata\\local\\temp\\tmpepvljn\\tmp_R
eadFileWithPull': failed with exit status 1 and output:\n- adb: error: remote ob
ject '/sys/kernel/debug/tracing/tracing_on' does not exist\n",)
CRITICAL:root:(TimeoutThread-3-for-Thread-11) Exception on ReadFile(YVF6R1741500
0417, /sys/kernel/debug/tracing/tracing_on, retries=3, timeout=30), attempt 3 of
 4: AdbCommandFailedError("(device: YVF6R17415000417) adb pull /sys/kernel/debug
/tracing/tracing_on 'c:\\users\\admini~1\\appdata\\local\\temp\\tmpiyef2y\\tmp_R
eadFileWithPull': failed with exit status 1 and output:\n- adb: error: remote ob
ject '/sys/kernel/debug/tracing/tracing_on' does not exist\n",)
Exception in thread Thread-11:
Traceback (most recent call last):
  File "D:\software\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "D:\software\Python27\lib\threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 196, in _collect_and_preprocess
    trace_data = self._collect_trace_data()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 256, in _collect_trace_data
    result = self._stop_collect_trace()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\tracing_agents\atrace_agent.py", line 247, in _stop_collect_trace
    if int(self._device_utils.ReadFile(is_trace_enabled_file)):
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\decorators.py", line 57, in timeout_retry_wrapper
    retry_if_func=retry_if_func)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\timeout_retry.py", line 157, in Run
    error_log_func=error_log_func)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\reraiser_thread.py", line 186, in JoinAll
    self._JoinAll(watcher, timeout)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\reraiser_thread.py", line 158, in _JoinAll
    thread.ReraiseIfException()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\reraiser_thread.py", line 81, in run
    self._ret = self._func(*self._args, **self._kwargs)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\utils\timeout_retry.py", line 150, in <lambda>
    child_thread = reraiser_thread.ReraiserThread(lambda: func(*args, **kwargs),

  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\decorators.py", line 47, in impl
    return f(*args, **kwargs)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\device_utils.py", line 1781, in ReadFile
    return self._ReadFileWithPull(device_path)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\device_utils.py", line 1733, in _ReadFileWithPull
    self.adb.Pull(device_path, host_temp_path)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\sdk\adb_wrapper.py", line 474, in Pull
    self._RunDeviceAdbCmd(cmd, timeout, retries)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\sdk\adb_wrapper.py", line 301, in _RunDeviceAdbCmd
    check_error=check_error)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\decorators.py", line 51, in timeout_retry_wrapper
    return impl()
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\decorators.py", line 47, in impl
    return f(*args, **kwargs)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\..\..\devil\devil\android\sdk\adb_wrapper.py", line 281, in _RunAdbCmd
    args, output, status, device_serial)
AdbCommandFailedError: (device: YVF6R17415000417) adb pull /sys/kernel/debug/tra
cing/tracing_on 'c:\users\admini~1\appdata\local\temp\tmp_ygea9\tmp_ReadFileWith
Pull': failed with exit status 1 and output:
- adb: error: remote object '/sys/kernel/debug/tracing/tracing_on' does not exis
t


Outputting Systrace results...
Tracing complete, writing results
Traceback (most recent call last):
  File "systrace.py", line 49, in <module>
    sys.exit(run_systrace.main())
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\run_systrace.py", line 201, in main
    main_impl(sys.argv)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\run_systrace.py", line 198, in main_impl
    controller.OutputSystraceResults(write_json=options.write_json)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\systrace_runner.py", line 69, in OutputSystraceResults
    self._out_filename)
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\output_generator.py", line 99, in GenerateHTMLOutput
    html_file.write(_ConvertToHtmlString(result.raw_data))
  File "D:\software\Android\SDK\platform-tools\systrace\catapult\systrace\systra
ce\output_generator.py", line 121, in _ConvertToHtmlString
    raise ValueError('Invalid trace result format for HTML output')
ValueError: Invalid trace result format for HTML output
systrace-example-error

實戰總結:

1.測試發現目前運行Systrace命令只能使用Python2.7而不能使用Python3.X。

2.第一次運行Systrace命令時報錯:ImportError: No module named win32con。
解決方法:經過命令pip install pypiwin32或者python -m pip install pypiwin32 來安裝pypiwin32。
若是你沒有pip,請參考下面步驟:
下載(https://pypi.python.org/pypi/pip);
解壓;
安裝(Python setup.py install);
添加環境變量:D:\software\Python27\Scripts。
PS:個人電腦是裝了2個Python,一個Python2.7,一個Python3.5,結果執行pip install pypiwin32命令時把模塊安裝到了Python3.5裏,因而經過Python2.7執行Systrace命令時發現依然報錯。因此請確認你的pypiwin32確實安裝到了Python2.7裏(最好的辦法就是安裝前先卸載Python3.5)

3.解決了上面報錯後,發現又報錯:ValueError: Invalid trace result format for HTML output。查看stackoverflow後也沒找到合適的解決方案。

相關文章
相關標籤/搜索