【工利其器】必会工具之(三)systrace篇(1)官网翻译

前言

       Android 开发者官网中对systrace(Android System Trace)有专门的介绍,本篇文章作为systrace系列的开头,笔者先不做任何介绍,仅仅翻译一下官网的介绍。在后续的文章中再整理一份学习教程,以及笔者的实践经历。官网中对该工具的介绍文档路径为【https://developer.android.google.cn/studio/command-line/systrace?hl=en#java】。或者在进入到官网的首页后,按照Android Developers > Android Studio > USER GUIDE > Command line tools > systrace的路径访问该文档。

       该文档主要包含如下内容:

 

一、systrace简述

       systrace命令允许你收集和检查在你的设备上运行的所有系统级别进程的定时信息。它联合Android内核(比如CPU调度程序)、磁盘活动和app线程,生成一份HTML报告,如图1所示:

  图1:一个systrace的HTML报告案例,它显示了与app 5秒钟的交互。这份报告高亮显示了那些systrace认为可能没有被适当地渲染的帧。

       这份报告提供了一份在给定时间段内Android设备系统进程的全局图。它也检查了被捕获的追踪信息,用于高亮显示它观察到的问题,比如显示动作或动画时的UI jank(笔者注:界面来不及刷新导致的卡塞空白现象),以及提供修复这些问题的建议。但是,systrace不会收集在app进程内与代码执行相关的信息。 关于你的app正在执行哪些方法以及它占用了多少CPU资源的详情,请查看 【Android Studio CPU profiler】。你也可以使用CPU Profiler来生成追踪日志,导出并检查它们。

       这份文档解释了怎样从命令行生成systrace报告,操作由工具生成的trace文件,并且使用它们分析及改善应用的UI性能。

★注意:在运行于Android 9(API level 28)或者更高的设备上,你可以使用一个叫做System Tracing的系统app在设备上记录system trace。

      为了运行systrace,请完成下面的步骤:

      systrace工具在Android SDK Tools 包中提供,其路径为 android-sdk/platform-tools/systrace/

 

二、语法

       为了给app生成HTML报告,你需要在命令行中使用如下的语法运行systrace:

$python systrace.py [option][categories]

       例如,如下的命令行调用了systrace用于记录设备活动,并且生成一份名为 mynewtrace.html 的HTML报告。该categories列表对大多数设备而言是一个合理的默认列表。

$ python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \ binder_driver hal dalvik camera input res
★ 提示:如果你想看输出的trace中任务的名字,你的命令参数中必须包含“sched” category。

 想查看你连接的设备所支持的category列表,请运行下面的命令:

$ python systrace.py --list-categories

 如果你没有指定任何的category或者option,systrace会生成一个包含所有可用category的报告并且使用默认的设置。这些可用的category依赖于你所连接的正在使用的设备。

  1、全局option

 

全局option描述
-h | --help 显示帮助信息
-l | --list-categories 列出你连接的设备所支持的可用category

  2、命令和命令选项

Commands and options Description
-o file 把HTML trace报告写入到指定file。如果你不指定这个选项,systrace将会把你的报告保存到和systrace.py相同的根目录下,并且命名为trace.html。
-t N | --time=N 追踪设备活动N秒钟。如果你不指定该选项,systrace将会提示你在命令行中按Enter键结束追踪。
-b N | --buf-size=N 使用一个大小为N KB的trace缓存。这个选项可以让你限制追踪过程中收集的数据的大小。
-k functions
| --ktrace=functions
追踪指定内核函数的活动,在一个以逗号隔开的列表中列出。
-a app-name
| --app=app-name

 激活追踪app,这些app在以逗号分隔的进程名列表中被指定列出。这些app必须包含来自于Trace 类的追踪检测调用。

无论何时你profile你的应用,你都应该指明这个选项,很多库(比如RecyclerView)都包含了追踪检测调用,

当你激活应用级别的追踪时,这些调用提供了有用的信息。想了解更多信息,请阅读关于怎样“检测你的app代码”这部分内容。

--from-file=file-path 从文件当中创建一个交互式的HTML报告,比如包含原始trace数据的TXT文件,而不是运行实时追踪。
-e device-serial
| --serial=device-serial
在指定连接的设备上进行追踪,该设备由设备序列号来识别。
categories 包含你所指定的系统进程的追踪信息,比如gfx表示用于图像渲染的系统进程。你可以通过加上 -l 命令运行systrace来查看你所连接的设备上可用的服务列表。

 

三、调查用户界面性能问题

       systrace 在检查你的app的用户界面性能方面尤其有用,因为它能够分析你的代码和帧率来验证问题区域和建议可能的解决方案。首先,按照如下步骤进行:

      1)在你连接的设备上运行你的app。

      2)用下面的命令运行systrace,

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

       这个例子会追踪你的app 10秒钟。

      3)当systrace正在运行的时候,和你的app进行交互(笔者注:即操作你的app)。

      4)在你定义的限定时间过去后,比如该例中的10秒,systrace会生成一个HTML 报告。

      5)使用一个web浏览器打开这份HTML报告。

       通过和这份报告交互,你可以检查设备在这段记录时间内的CPU使用情况。为了帮助操作HTML报告,请查看“键盘快捷键”这一部分,或者点击报告右上角的"?"按钮。

       下面这一部分解释了怎样检查报告中的信息,从而找到并修复UI性能问题。

  1、检查帧和警告

       如图2所示,报告列出了每一个渲染UI帧的进程并且沿着时间线指出了所有的渲染帧。这些帧在16.6毫秒内渲染一帧, 需要维持一个稳定的每秒60帧的帧率,在报告中他们用绿色的圆圈表示。那些渲染超过16.6毫秒的帧则用黄色或者红色圆圈表示。

图2:长时间运行的帧放大后的systrace显示

★ 注意:在运行版本为Android5.0(API level 21)或更高的设备上,UI线程和渲染线程之间用于渲染帧的工作是分离的。在之前的版本中,创建帧的所有工作都是在UI线程中完成的。

       点击一个Frame圆圈(带有F标记的圆圈,后面简称圆圈)会让它变成高亮并且提供额外的信息,这些信息是关于系统渲染这一帧做所的工作,包括警告。同时也会向你显示渲染这一帧过程中系统正在执行的方法,所以你可以调查引起那些UI jank的方法。

图3:选择有问题的帧,一个警告会出现在trace报告下面来标识问题。

       当你选择一个渲染较慢的帧,你会在报告面板底部看到一个警告。图3中显示的警告唤起了该帧主要的问题,而该帧在ListView内回收和重新绑定时花费了太多时间。有一些链接指向trace中有关事件,他们解释了更多关于这段时间内系统做了些什么。

       为了查看该工具在你的trace中发现的每一个警告,以及该设备触发每一个警告的次数,请点击窗口最右边的“Alerts”标签,如图4所示。这个“Alerts”面板会帮你查看在这份trace中发生了什么问题,以及引起UI jank的频率。把这个面板当成一个bug列表去修复。通常,一个在某区域微小的改变或者改善可能消除你app中整个警告类型。

图4:点击右侧的“Alert”按钮将显示“alter”标签

       如果你看到在UI线程中做了太多的工作,你需要找出是哪个方法消费了太多的CPU时间。有一种方法就是添加trace标记(查看“检测你的app代码”这一节)到那些你认为有可能导致这些瓶颈的方法中,用来查看那些在systrace中出现的功能调用。如果你不确定在UI线程中哪些方法可能导致瓶颈,使用Android Studio CPU profier 。你可以生成trace日志并且使用CPU Profiler来导入和检测它们。  

  2、HTML报告键盘快捷键

     下面的表格列出了当浏览systrace HTML报告时可用的键盘快捷键。

按键描述
W 放大trace时间轴。
S 缩小trace时间轴。
A 在时间轴上向左平移。
D 在时间轴上向右平移。
E 将trace时间轴置于当前鼠标的中心。
G 在当前选择的任务的开始处显示网格。
Shift + G 在当前选择的任务的结尾处显示网络.
Right Arrow 在当前选择的时间轴上选择下一个事件(笔者注:当聚焦在frame 圆圈上时容易看到效果)。
Left Arrow 爱当前选择的时间轴上选择前一个事件。

 

四、检测你的app代码

       因为systrace仅仅只在系统级别给你显示进程信息,所以在HTML报告中很难知道在给定的时间内你的app执行了哪些方法。在Android4.3(API level 18)已经更高的版本中,你可以在你的代码中使用Trace类来标记HTML报告中执行的事件。你没有必要用systrace去检测你的代码来记录trace,但是这样做可以帮助你看到你的app代码中哪部分可能引起线程挂起或者UI jank。这种途径有别于使用Debug类,Trace类简单地添加标签到systrace报告,但是Debug类通过.trace文件能帮助你检测详细的app CPU使用情况。

       为了生成包含你检测trace事件的systrace HTML报告,你需要运行结合-a或者--app命令行运行systrace,并指明你的app的包名。

       下面的代码实例向你展示了怎样使用Trace类来标记方法的执行,包括在那个方法内的两段嵌套的代码块:

 1 public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
 2     ...
 3     @Override
 4     public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 5         Trace.beginSection("MyAdapter.onCreateViewHolder");
 6         MyViewHolder myViewHolder;
 7         try {
 8             myViewHolder = MyViewHolder.newInstance(parent);
 9         } finally {
10             // In 'try...catch' statements, always call endSection()
11             // in a 'finally' block to ensure it is invoked even when an exception
12             // is thrown.
13             Trace.endSection();
14         }
15         return myViewHolder;
16     }
17 
18    @Override
19     public void onBindViewHolder(MyViewHolder holder, int position) {
20         Trace.beginSection("MyAdapter.onBindViewHolder");
21         try {
22             try {
23                 Trace.beginSection("MyAdapter.queryDatabase");
24                 RowItem rowItem = queryDatabase(position);
25                 dataset.add(rowItem);
26             } finally {
27                 Trace.endSection();
28             }
29             holder.bind(dataset.get(position));
30         } finally {
31             Trace.endSection();
32         }
33     }
34 ...
35 }

 

★ 注意:当你调用beginSection(String)多次,调用endSection()只结束最近一次调用的beginSection(String)。所以,对于嵌套的代码,比如上面例子中的,你需要确定你适当地匹配了一次beginSection()调用和一次endSection()。另外,你不能在一个线程中调用beginSection(),却在另外一个线程中结束它,你必须在相同的线程中调用endSection()。

 

结语

       到这里,该篇文章就翻译结束了,后面的文章中,笔者将根据自己的理解和实际操作,继续对System Trace进行阐述。限于笔者的水平,如有翻译不准确或不妥当的地方,望不吝赐教。

posted @ 2019-04-05 19:24  宋者为王  阅读(3022)  评论(0编辑  收藏