Systrace

Google 鏈接
Systrace是一個platform-provided工具,用於記錄設備在短時間內的活動。 允許在系統級別上收集和檢查設備上運行的所有進程的時間信息。將來自Android內核的數據(如CPU調度程序、磁盤活動和應用程序線程)結合起來生成一個HTML報告,幫助確定如何最好地提高應用程序或遊戲的性能。該報告突出了它觀察到的問題(如在顯示動作或動畫時的ui jank),並且提供了有關如何修復這些問題的建議。但是,Systrace不會在應用程序進程中收集有關代碼執行的信息。有關應用正在執行的方法以及使用多少CPU資源的詳細信息使用Android Studio CPU Profiler,你也可以生成trace logs後使用Profiler查看。
    三種使用方式:命令行調用,System Tracing APP,DDMS
    在Android5.0(API級別21)或更高版本的設備上,渲染一個frame的工作被UI Thread和Render Thread拆分。在以前的版本上,創建一個frame的所有工作都是在UI thread上完成的。
    在解決應用程序中與性能相關的錯誤(如啓動緩慢、轉換緩慢或UI jank)時,記錄跟蹤尤其有用。

  • 命令行調用
    Systrace command在android sdk tools包中提供,位於android-sdk/platform-tools/systrace/
    啓動systrace,通過以下步驟:

    1. 下載安裝Android Studio,然後下載SDK並更新。
      不一定要使用Android Studio,但是現在的SDK只能通過Android Studio更新。
    2. 安裝python並將其包含在工作站的執行路徑中。添加path D:\Python27,cmd 中輸入python ,有信息
    3. python 2.7.9以後的自帶pip,添加path D:\Python27\Scripts,cmd 中輸入pip,有信息
    4. 通過USB調試連接到一個Android 4.3(API)或者 4.3以上的設備。
    5. 生成mynewtrace.html的文件; 當前目錄: D:\adt\SDK\platform-tools\systrace
      python systrace.py -o mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
      
    6. 查看類別:其他命令查看鏈接
      python systrace.py --list-categories
      
      遇到缺少win32con, pip install -i https://pypi.douban.com/simple pypiwin32

      win32con

       

      需要啓動模擬器或者連接設備

       

  • System Tracing APP
        在Android 9.0(API級別28)或更高版本的設備上,可以使用 System Tracing的 System App在設備上記錄系統跟蹤。系統跟蹤將設備的活動保存到滾動緩衝區,該緩衝區可保存10-30秒的事件值。

    1. 開啓開發者選項
    2. 打開開發者選項,選擇System Tracing;開啓Show Quick Setting tile的Switch Button。
    3. 或者,選擇要跟蹤的系統和傳感器調用的類別,並選擇緩衝區大小(以KB爲單位)。選擇與正在測試的用例相對應的類別,例如用於測試藍牙操作的音頻類別。
    4. 打開快速設置面板中的System Tracing 如下圖System Tracing,或者打開開發者選項中的System Tracing並開啓Record trace的Switch Button。
    5. 通知面板會出現一個System Tracing通知,點擊通知面板或者快速設置面板中的System Tracing,停止tracing
    6. 系統將顯示一個“saving trace”的新通知。保存完成後,系統將取消通知並顯示第三個通知,System Tracing已保存,並且已準備好分享你的trace。
    7. 點擊通知,顯示分享對話框,點擊share可以通過郵箱分享
    8. adb 命令下載,下載文件是.ctrace
      cd /path-to-traces-on-my-dev-machine
      adb pull /data/local/traces/ .
      
    9. 生成html文件;當前目錄: D:\adt\SDK\platform-tools\systrace
      cd /path-to-traces-on-my-dev-machine
      python systrace.py --from-file trace-file-name.ctrace
      

    Developer Options

     

    Show Quick Setting tile

     

    System Tracing

     

    Trace saved

     

    Share Trace

     

    生成html文件

  • DDMS
    啓動DDMS後,點擊Capture system wide trace using Android systrace按鈕後點擊OK。
    Destination File: 生成文件存放哪裏;
    Trace duration (seconds): 追蹤週期時間
    Trace Buffer Size(kb): 追蹤緩衝區大小
    Enable Application Traces from: 選擇需要追蹤的進程
    Commonly Used Tags: 常用標籤     具體內容看圖
    Advanced Options: 高級選項     具體內容看圖

    Capture system wide trace using Android systrace

  • 報告分析

    1. 左邊呈現是UI Frames的每個進程,選擇你要觀察的APP,右邊是沿時間線指示呈現每個Frame。
      綠色圓表示: 16.6毫秒內渲染完成的Frames。
      黃色或紅色圓表示: 渲染時間超過16.6毫秒的Frames。
    2. 單擊一個frame 圓將高亮顯示它,並提供系統渲染該frame所做的相關信息包括系統在呈現該幀時正在執行的方法(因此可以調查這些導致ui-jank的方法)和Alert。 Alert指出主要問題是在ListView回收和重新綁定中花費太多時間。Alert中有相關事件的鏈接,這些鏈接可以更詳細地解釋系統在這段時間內所做的工作。如圖Android System Trace A Frame
    3. 若要查看每個Alert,以及觸發每個Alert的次數,請單擊窗口右側的Alerts選項卡,如圖Android System Trace Alert 所示。Alerts panel中查看跟蹤出現的問題,以及它們對jank的影響頻率。將Alerts panel視爲要修復的錯誤列表。通常,一個領域的微小變化或改進可以消除應用中的整個Alerts。
    4. 如果UI Thread做了太多的工作,需要找出哪些方法佔用了太多的CPU時間:
      爲導致這些瓶頸的方法添加自定義事件,查看這些函數調用次數在Systrace中(具體操作下一段定義自定義事件)。
      如果不確定哪些方法會導致UI Thread出現瓶頸,請使用Android Studio Cpu Profiler,。

Capture system wide trace using Android systrace

Android System Trace

Android System Trace Zoom

Android System Trace A Frame

Android System Trace Alert

  • 定義自定義事件
    1. 在Android 4.3(API級別18)及更高版本中,使用代碼中的Trace類在HTML報告中標記執行事件。需要使用-a或--app命令行選項運行Systrace,並指定應用程序的包名稱。

      python systrace.py -a com.lqr.wechat -b 16384 -o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res 
      

          下面的代碼示例演示如何使用Trace類來標記方法的執行,包括該方法中的兩個嵌套代碼塊;多次調用BeginAtomon(String)時,調用EndSection()只結束最近調用的BeginAtomon(String)方法。因此,對於嵌套調用(如下面示例中的調用),需要確保每個對BeginAtomion()的調用都與對EndSection()的調用正確匹配。此外,不能在一個線程上調用BeginAtomon()並從另一個線程結束它,必須從同一個線程調用EndSection()。

      public  class  MyAdapter  extends  RecyclerView.Adapter<MyViewHolder>  {  
          ...
          @Override  
          public  MyViewHolder onCreateViewHolder(ViewGroup parent,  int viewType)  {  
              Trace.beginSection("MyAdapter.onCreateViewHolder");  
              MyViewHolder myViewHolder;  
              try  { 
                 myViewHolder =  MyViewHolder.newInstance(parent); 
              }  finally  {  
                  // In 'try...catch' statements, always call `[endSection()](https://developer.android.google.cn/reference/android/os/Trace.html#endSection())`  
                  // in a 'finally' block to ensure it is invoked even when an exception  // is thrown.  
                  Trace.endSection(); 
              }  
              return myViewHolder;  
          } 
      
          @Override  
          public  void onBindViewHolder(MyViewHolder holder,  int position)  { 
              Trace.beginSection("MyAdapter.onBindViewHolder");  
              try  {  
                  try  {  
                      Trace.beginSection("MyAdapter.queryDatabase");  
                      RowItem rowItem = queryDatabase(position); 
                      dataset.add(rowItem);  
                  }  finally  {  
                      Trace.endSection(); 
                  } 
                  holder.bind(dataset.get(position));
              }  finally  {  
                  Trace.endSection();  
              } 
          } 
          ... 
       }
      
    2. Android 6.0(API級別23)及更高版本支持調用native層tracing
      API 通過引入trace.h,將trace事件寫入系統緩衝區,然後使用Systrace進行分析。Android 6.0到4.3可以嘗試通過JNI調用。

      • 爲ATrace functions定義方法指針
      #include <android/trace.h>
      #include <dlfcn.h>
      
      void *(*ATrace_beginSection) (const char* sectionName);
      void *(*ATrace_endSection) (void);
      
      typedef void *(*fp_ATrace_beginSection) (const char* sectionName);
      typedef void *(*fp_ATrace_endSection) (void);
      
      • 在運行時加載ATrace符號,通常在對象構造函數中執行這個過程,出於安全原因,僅在應用程序或遊戲的調試版本中包含對dlopen()的調用。
      // Retrieve a handle to libandroid.
      void *lib = dlopen("libandroid.so", RTLD_NOW || RTLD_LOCAL);
      
      // Access the native tracing functions.
      if (lib != NULL) {
         // Use dlsym() to prevent crashes on devices running Android 5.1
         // (API level 22) or lower.
          ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>(dlsym(lib, "ATrace_beginSection"));
          ATrace_endSEction = reinterpret_cast<fp_ATrace_endSection>(dlsym(lib, "ATrace_endSection"));
      } 
      
      • 在自定義事件的開始和結束調用atrace_begindomon() 和atrace_endsection()
      #include <android/trace.h>
      
      char *customEventName = new char[32]; 
      sprintf(customEventName, "User tapped %s button", buttonName);
      
      ATrace_beginSection(customEventName);
      // Your app or game's response to the button being pressed.
      ATrace_endSection();
      
      • 跟蹤整個方法
        當檢測調用堆棧或函數計時時,會發現跟蹤整個函數很有用。使用ATRACE_CALL()宏使這種類型的跟蹤更容易設置。此外,通過創建try-catch塊,這種宏允許跳過被跟蹤函數引發的異常或提前調用return的情況。
        1. 定義宏
          #define ATRACE_NAME(name) ScopedTrace ___tracer(name)
          
          // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
          #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
          
          class ScopedTrace {
            public:
              inline ScopedTrace(const char *name) {
                ATrace_beginSection(name);
              }
          
              inline ~ScopedTrace() {
                ATrace_endSection();
              }
          };
          
        2. 調用宏在被跟蹤方法中
          void myExpensiveFunction() {
            ATRACE_CALL();
            // Code that you want to trace.
          }
          
      • 給你的線程名稱
        給事件發生的每個線程提供名稱,可以更容易地識別屬於特定操作的線程。
        #include <pthread.h>
        
        static void *render_scene(void *parm) {
            // Code for preparing your app or game's visual components.
        }
        
        static void *load_main_menu(void *parm) {
            // Code that executes your app or game's main logic.
        }
        
        void init_threads() {
            pthread_t render_thread, main_thread;
        
            pthread_create(&render_thread, NULL, render_scene, NULL);
            pthread_create(&main_thread, NULL, load_main_menu, NULL);
        
            pthread_setname_np(render_thread, "MyRenderer");
            pthread_setname_np(main_thread, "MyMainMenu");
        }
        

 

發佈了67 篇原創文章 · 獲贊 45 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章