• Android UI性能测试——使用 Systrace 查找问题


    一 官方文档翻译

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

    systrace命令允许您在系统级别上收集和检查所有运行在设备上的进程的耗时信息。它结合了来自Android内核的数据,例如CPU调度程序,磁盘活动和app线程,最后生成HTML报告,类似于图1中所示。

    图1:systrace HTML示例报告,程序默认抓取5秒内应用和系统的消耗。该报告突出显示了systrace认为的异常帧。

    此报告提供了在给定时间段内Android设备的系统进程的总体情况。它还检查捕获的跟踪信息以突出显示它所发现的问题,例如在显示动作或动画时的UI jank,并提供有关如何修复它们的建议。但是, systrace工具不会收集有关app进程中代码执行的信息。有关app正在执行的方法以及它使用的CPU资源的更多详细信息,请使用Android Studio自带的的CPU profiler,您还可以生成跟踪日志并使用CPU profiler导入和检查它们。

    本文档说明如何从命令行通过systrace生成报告,查看生成的跟踪文件,并使用它们分析和改进app用户界面(UI)的性能。

    注意:在运行Android 9(API级别28)或更高版本的设备上,您还可以使用名为“系统跟踪”的系统应用程序来记录设备上的系统跟踪。

    要运行systrace,请完成以下步骤

    1.从Android Studio下载并安装最新的Android SDK工具。
    2.安装Python并配置环境变量。
    3.使用USB调试将运行Android 4.3(API级别18)或更高版本的设备连接到开发系统。

    Systrace是Android SDK中自带的一个命令工具,位于android-sdk/platform-tools/systrace/

    语法

    要为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:UsersAdministrator>adb devices # 查看设备
    List of devices attached
    GSL7N16B10000581        device
    
    C:UsersAdministrator>D: # 进入D盘
    
    D:>cd softwareAndroidSDKplatform-toolssystrace # 进入Systrace目录
    
    D:softwareAndroidSDKplatform-toolssystrace>dir # 查看该目录:可看到有systrace.py文件
     驱动器 D 中的卷是 软件
     卷的序列号是 A80B-3BC5
    
     D:softwareAndroidSDKplatform-toolssystrace 的目录
    
    20181130 周五  14:25    <DIR>          .
    20181130 周五  14:25    <DIR>          ..
    20181122 周四  10:32    <DIR>          catapult
    20181122 周四  10:32            11,738 NOTICE
    20181122 周四  10:32             1,456 systrace.py
    20181122 周四  10:32                41 UPSTREAM_REVISION
                   3 个文件         13,235 字节
                   3 个目录 28,734,406,656 可用字节
    
    
    D:softwareAndroidSDKplatform-toolssystrace>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:softwareAndroidSDKplatform-toolssystrace>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:softwarePython27lib	hreading.py", line 801, in __bootstrap_inner
        self.run()
      File "D:softwarePython27lib	hreading.py", line 754, in run
        self.__target(*self.__args, **self.__kwargs)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 196, in _collect_and_preprocess
        trace_data = self._collect_trace_data()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 256, in _collect_trace_data
        result = self._stop_collect_trace()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 243, in _stop_collect_trace
        large_output=True, check_return=True, timeout=ADB_LARGE_OUTPUT_TIMEOUT)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddecorators.py", line 57, in timeout_retry_wrapper
        retry_if_func=retry_if_func)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils	imeout_retry.py", line 157, in Run
        error_log_func=error_log_func)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils
    eraiser_thread.py", line 186, in JoinAll
        self._JoinAll(watcher, timeout)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils
    eraiser_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:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce
    un_systrace.py", line 201, in main
        main_impl(sys.argv)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce
    un_systrace.py", line 198, in main_impl
        controller.OutputSystraceResults(write_json=options.write_json)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    cesystrace_runner.py", line 69, in OutputSystraceResults
        self._out_filename)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ceoutput_generator.py", line 99, in GenerateHTMLOutput
        html_file.write(_ConvertToHtmlString(result.raw_data))
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ceoutput_generator.py", line 121, in _ConvertToHtmlString
        raise ValueError('Invalid trace result format for HTML output')
    ValueError: Invalid trace result format for HTML output
    
    D:softwareAndroidSDKplatform-toolssystrace>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:
    - adb: error: remote ob
    ject '/sys/kernel/debug/tracing/tracing_on' does not exist
    ",)
    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:
    - adb: error: remote ob
    ject '/sys/kernel/debug/tracing/tracing_on' does not exist
    ",)
    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:
    - adb: error: remote ob
    ject '/sys/kernel/debug/tracing/tracing_on' does not exist
    ",)
    Exception in thread Thread-11:
    Traceback (most recent call last):
      File "D:softwarePython27lib	hreading.py", line 801, in __bootstrap_inner
        self.run()
      File "D:softwarePython27lib	hreading.py", line 754, in run
        self.__target(*self.__args, **self.__kwargs)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 196, in _collect_and_preprocess
        trace_data = self._collect_trace_data()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 256, in _collect_trace_data
        result = self._stop_collect_trace()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce	racing_agentsatrace_agent.py", line 247, in _stop_collect_trace
        if int(self._device_utils.ReadFile(is_trace_enabled_file)):
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddecorators.py", line 57, in timeout_retry_wrapper
        retry_if_func=retry_if_func)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils	imeout_retry.py", line 157, in Run
        error_log_func=error_log_func)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils
    eraiser_thread.py", line 186, in JoinAll
        self._JoinAll(watcher, timeout)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils
    eraiser_thread.py", line 158, in _JoinAll
        thread.ReraiseIfException()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils
    eraiser_thread.py", line 81, in run
        self._ret = self._func(*self._args, **self._kwargs)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilutils	imeout_retry.py", line 150, in <lambda>
        child_thread = reraiser_thread.ReraiserThread(lambda: func(*args, **kwargs),
    
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddecorators.py", line 47, in impl
        return f(*args, **kwargs)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddevice_utils.py", line 1781, in ReadFile
        return self._ReadFileWithPull(device_path)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddevice_utils.py", line 1733, in _ReadFileWithPull
        self.adb.Pull(device_path, host_temp_path)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroidsdkadb_wrapper.py", line 474, in Pull
        self._RunDeviceAdbCmd(cmd, timeout, retries)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroidsdkadb_wrapper.py", line 301, in _RunDeviceAdbCmd
        check_error=check_error)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddecorators.py", line 51, in timeout_retry_wrapper
        return impl()
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroiddecorators.py", line 47, in impl
        return f(*args, **kwargs)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce....devildevilandroidsdkadb_wrapper.py", line 281, in _RunAdbCmd
        args, output, status, device_serial)
    AdbCommandFailedError: (device: YVF6R17415000417) adb pull /sys/kernel/debug/tra
    cing/tracing_on 'c:usersadmini~1appdatalocal	emp	mp_ygea9	mp_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:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce
    un_systrace.py", line 201, in main
        main_impl(sys.argv)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ce
    un_systrace.py", line 198, in main_impl
        controller.OutputSystraceResults(write_json=options.write_json)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    cesystrace_runner.py", line 69, in OutputSystraceResults
        self._out_filename)
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ceoutput_generator.py", line 99, in GenerateHTMLOutput
        html_file.write(_ConvertToHtmlString(result.raw_data))
      File "D:softwareAndroidSDKplatform-toolssystracecatapultsystracesystra
    ceoutput_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:softwarePython27Scripts。
    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后也没找到合适的解决方案。

  • 相关阅读:
    学期总结
    C语言I博客作业09
    C语言I博客作业08
    C语言I博客作业07
    C语言I博客作业06
    C语言I博客作业06
    C语言I博客作业05
    C语言I博客作业04
    作业02
    c语言 学习笔记之二 选择题2
  • 原文地址:https://www.cnblogs.com/zhengna/p/10043531.html
Copyright © 2020-2023  润新知