简介
trace [tres] vt.跟踪,追踪; 追溯,探索; 探索; 查找; n.痕迹; 痕迹,踪迹; 微量,极少量;
1
1
1
trace [tres] vt.跟踪,追踪; 追溯,探索; 探索; 查找; n.痕迹; 痕迹,踪迹; 微量,极少量;
Systrace是Android4.1中新增的性能数据采样和分析工具,它可帮助开发者收集Android关键子系统(如SurfaceFlinger、WindowManagerService等Framework部分关键模块、服务、View系统等)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。
Systrace允许你监视和跟踪Android系统的行为,它会告诉你系统都在哪些工作上花费时间、CPU周期都用在哪里,甚至你可以看到每个线程、进程在指定时间内都在干嘛。它同时还会突出观测到的问题,从垃圾回收到渲染内容都可能是问题对象,甚至提供给你建议的解决方案。
Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成:
- 内核部分:Systrace利用了Linux Kernel中的ftrace功能,所以,如果要使用Systrace的话,必须开启kernel中和ftrace相关的模块。
- 数据采集部分:Android定义了一个Trace类,应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。
- 数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集ftrace统计数据并生成一个结果网页文件供用户查看。从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace。
启动Systrace
Systrace是Android4.1中新增的,所以要求要生成Trace的手机在Android 4.1以上。
Systrace工具可以在sdk/platform-tools/ 中找,源码位于external/chromium-trace下面。
Systrace可以通过命令行,Eclipse,Android Studio等方式启动。
【Using Android Studio】
- 1.In Android Studio, open an Android application project. 1.Open the Device Monitor by selecting Tools > Android > Monitor.
- 2.In the Devices tab, select the device on which to run a trace. If no devices are listed, make sure your device is connected via USB cable and that debugging is enabled on the device.
- 3.Click the Systrace icon at the top of the Devices panel to configure tracing.
- 4.Set the tracing options and click OK to start the trace.
【Using Eclipse】
- 1.In Eclipse, open an Android application project. 1.Switch to the DDMS perspective, by selecting Window > Perspectives > DDMS.
- 2.In the Devices tab, select the device on which to run a trace. If no devices are listed, make sure your device is connected via USB cable and that debugging is enabled on the device.
- 3.Click the Systrace icon at the top of the Devices panel to configure tracing.
- 4.Set the tracing options and click OK to start the trace.
【Using Device Monitor】
- 1.Navigate to your SDK tools/ directory. 1.Run the monitor program.
- 2.In the Devices tab, select the device on which to run a trace. If no devices are listed, make sure your device is connected via USB cable and that debugging is enabled on the device.
- 3.Click the Systrace icon at the top of the Devices panel to configure tracing.
- 4.Set the tracing options and click OK to start the trace.
生成Trace分析文档
生成Trace分析文档的步骤如下:
- 打开手机调试模式,连接手机,运行APP
- 启动Systrace
- 手机准备好你要进行抓取的界面
- 配置文件路径、抓取时间等
- 点击确定后开始操作手机,在时间到了后会自动生成报表(这个文件至少也有好几兆)
- 使用Chrome(其他浏览器很可能打不开)将这个文件打开进行分析
Systrace的一些配置
- duration:抓取时间,通常设置5秒,并在5秒内重现问题,时间太短会导致问题重现时没有被抓到,时间太长会导致JavaHeap不够而无法保存。因此在能抓到问题点的情况下,时间越小越好。
- Buffer Size:存储Systrace的size,同样太小会导致信息丢失,太长会导致Java Heap不够而无法保存,建议【20480】。如果检测结果有异常,请调整Buffer Size的大小试试。
- Application:检测的应用,默认选择none
- Commonly Used Tag:常用标签,这部分TAG全部使能上。只关注显示情况的话 ,只选取Graphics和View System就可以。
- Advanced Options:高级选项。如果设备root了,可以看到更多的TAG,如eMMC commands/ Synchonization/Kernel Workqueues。
测试时发现,5.1的华为手机点击确定后报以下误:
Unexpected error while collecting system trace.
Unable to find trace start marker 'TRACE:':error opening /sys/kernel/debug/tracing/options/overwrite: No such file or directory
2
2
1
Unexpected error while collecting system trace.
2
Unable to find trace start marker 'TRACE:':error opening /sys/kernel/debug/tracing/options/overwrite: No such file or directory
而7.0的三星手机正常
打开Trace文档
可以用Chrome浏览器打开生成的trace文件。浏览时可以使用w/s/a/d快捷键来放大、缩小、移动等操作。可以点击右上角的?号来查看等多的操作。
Key | Description |
w | Zoom into the trace timeline. |
s | Zoom out of the trace timeline. |
a | Pan left on the trace timeline. |
d | Pan right on the trace timeline. |
e | Center the trace timeline on the current mouse location. |
g | Show grid at the start of the currently selected task. |
Shift+g | Show grid at the end of the currently selected task. |
Right Arrow | Select the next event on the currently selected timeline. |
Left Arrow | Select the previous event on the currently selected timeline. |
双击 |
Zoom into the trace timeline. |
Shift+双击 | Zoom out of the trace timeline. |
分析Trace文档--简介
纵轴代表着时间线,事件记录按照进程分组,同一个进程内按线程进行纵向拆分,每个线程记录自己的工作,可收缩/展开。
在本例中,一共有三个组:Kernel, SurfaceFlinger, App,他们分别以包名为标识。每个应用进程都会包含其中所有线程的记录信号,你可以看到从InputEvent到RenderThread都有。
除了进程和线程运行信息,还有两个重要信息:
【Frame】
在每个app进程,都有一个Frames行,正常情况以绿色的圆点表示。当圆点颜色为黄色或者红色时,意味着这一帧超过16.6ms(即发现丢帧),这时需要通过放大那一帧进一步分析问题。对于Android 5.0(API level 21)或者更高的设备,该问题主要聚焦在UI Thread和Render Thread这两个线程当中。对于更早的版本,则所有工作在UI Thread。
【Alerts】
Systrace能自动分析trace中的事件,并能自动高亮性能问题作为一个Alerts,建议调试人员下一步该怎么做。
比如对于丢帧时,点击黄色或红色的Frames圆点便会有相关的提示信息;另外,在systrace的最右侧,有一个Alerts tab可以展开,这里记录着所有的的警告提示信息。
当我们点击了Alerts或者点击右边的Alerts列表中的任何一点,我们可以看到,在界面的最底部,会有相对应的优化提示,以及可能会出现优化的视频教程链接。
比如上面的提示说你View的draw绘制花的时间太长了,然后我们可以根据Description来很明白的看到提示的内容是什么。
然后我们可以点击一块Frames中的F来查看,同样的它会生成一份跟Alerts类似的报告结果并放在界面的最低端。
我们可以通过按下m键查看这一帧到下一帧所花费的时间以及哪个方法被调用的最长。
可以明显看到这时间>16.6ms,大于系统要求UI的60fps水准所以系统会报出黄色的警告。
照样我们从Description中可以读出到底是哪里出了问题
Description :ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.
//ListView项目回收涉及inflating视图。 确保您的Adapter#getView() 循环使用传入的View,而不是构建新的View。
1
Description :ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.
2
//ListView项目回收涉及inflating视图。 确保您的Adapter#getView() 循环使用传入的View,而不是构建新的View。
根据系统提示,我们就能顺着这条路去找界面的代码哪里出现了不足从而优化完善。
当然Systrace无法帮你定位到代码里面的具体到某一行代码,但是我们可以通过Alerts和Frames来能基本上优化了不足的地方,然后我们可以根据TraceView来分析具体函数花了多长时间来进一步优化代码提高性能。
分析Trace文档--案例
上面介绍了Systrace中不同区域的功能,当然最有趣的还是Alerts和Frames两栏,让我们来选择最上方的Alerts瞧瞧:
这个警告指出了,有一个View#draw()方法执行了比较长的时间。我们可以在下面看到问题的描述、链接,甚至是相关的视频。
下面我们看Frames这一行,可以看到这里展示了被绘制出来的每一帧,并且用绿、黄、红三颜色来区分它们在绘制时的性能。我们选一个红色帧来瞅瞅:
在最下方,我们看到了与这一帧所相关的一些警告。
在这三个警告中,有一个是我们上面所提到的(View#draw())。接下来我们在这一帧处放大并在下方展开“Inflation during ListView recycling”这条警告:
我们可以看到警告部分的总耗时32毫秒,远高于了我们对保障60fps所需的16.6毫秒绘制时间。同时还有更多的ListView每个条目的绘制时间,大约是6毫秒每个条目,总共五个。而Description描述项中的内容会帮助我们理解问题,甚至提供问题的解决方案。回到我们上一张图片,我们可以在“inflate”这一个块区处放大,并且观察到底是哪些View在被填充过程中耗时比较严重。
下面是另外一个渲染过慢的实例:
在选择了某一帧之后,我们可以按“m”键来高亮这一帧,并且在上方看到了这一部分的耗时,如图,我们看到了这一阵的绘制总共耗时大约19毫秒。而当我们展开这一帧唯一的一个警告时,我们发现了“Scheduling delay”这条错误。
Scheduling delay(调度延迟)的意思就是一个线程在处理一块运算的时候,在很长一段时间都没有被分配到CPU上面做运算,从而导致这个线程在很长一段时间都没有完成工作。我们选择这一帧中最长的一块,从而得到更加详细的信息:
在红框区域内,我们看到了“Wall duration”,他代表着这一区块的开始到结束的耗时。之所以叫作“Wall duration”,是因为他就像是墙上的一个时钟,从线程的一开始就为你计时。
而CPU Duration一项中显示了实际CPU在处理这一区块所消耗的时间。
很显然,两个时间的差距还是非常大的。整个区块耗时18毫秒,而在这之中CPU只消耗了4毫秒的时间去运算。这就有点奇怪了,所以我们应该看一下在这整个过程之中,CPU去干吗了。
可以看到,所有四个线程都非常的繁忙。
选择其中的一个线程会告诉我们是哪个程序在占用他,在这里是一个包名为com.udinic.keepbusyapp的程序。在这里,由于另外一个程序占用CPU,导致了我们的程序未能获得足够的CPU资源。
但是这种情况其实是暂时的,因为被其他后台应用占用CPU的情况并不多见,但仍有其他应用的线程或是主线程占用CPU。而Systrace也只能为我们提供一个概览,他的深度是有限的。所以要找到我们app中到底是什么让我们的CPU繁忙,我们还要借助另一个工具——Traceview。
通过Trace类跟踪应用案例
Trace类能够让你在任何时候跟踪应用的一举一动。在你获取trace的过程中,Trace.beginSection()与Trace.endSection()之间代码工作会一直被追踪。
比如,我们在recyclerview的bindview中使用sleep模拟耗时操作,滚动时会出现明显卡顿
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onBindViewHolder(StaggredAdapter.MyHolder holder, int position) {
holder.mivItem.setImageResource(mlstPics.get(position));
Trace.beginSection("aaa");//生成的trace文件中,会在跟踪的代码段执行对应时间轴区间打上一个tag标记
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Trace.endSection();//Trace的begin与end必须在同一线程之中执行
}
x
1
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
2
@Override
3
public void onBindViewHolder(StaggredAdapter.MyHolder holder, int position) {
4
holder.mivItem.setImageResource(mlstPics.get(position));
5
6
Trace.beginSection("aaa");//生成的trace文件中,会在跟踪的代码段执行对应时间轴区间打上一个tag标记
7
try {
8
Thread.sleep(1000);
9
} catch (InterruptedException e) {
10
e.printStackTrace();
11
}
12
Trace.endSection();//Trace的begin与end必须在同一线程之中执行
13
}
使用systrace跟踪,生成如下分析图
找到分析进程对应的ui线程,线程的纵向信息记录的是调用关系,横向信息记录的是调用顺序,代码中的标记“aaa”标记了sleep的时间段,可以按m键高亮显示这个标记对应的时间段,可以看出以下信息:
- 丢帧,在aaa标记时段的前面,有一帧标红的frame,它跟下一个frame之间间隔事件很长(代码sleep中)
- 调用关系,RV滚动 ---> bindview ---> aaa(sleep)
2017-10-20
比如上面的提示说你View的draw绘制花的时间太长了,然后我们可以根据Description来很明白的看到提示的内容是什么。
然后我们可以点击一块Frames中的F来查看,同样的它会生成一份跟Alerts类似的报告结果并放在界面的最低端。
我们可以通过按下m键查看这一帧到下一帧所花费的时间以及哪个方法被调用的最长。
可以明显看到这时间>16.6ms,大于系统要求UI的60fps水准所以系统会报出黄色的警告。
照样我们从Description中可以读出到底是哪里出了问题
Description :ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.
//ListView项目回收涉及inflating视图。 确保您的Adapter#getView() 循环使用传入的View,而不是构建新的View。
1
Description :ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.
2
//ListView项目回收涉及inflating视图。 确保您的Adapter#getView() 循环使用传入的View,而不是构建新的View。
根据系统提示,我们就能顺着这条路去找界面的代码哪里出现了不足从而优化完善。
当然Systrace无法帮你定位到代码里面的具体到某一行代码,但是我们可以通过Alerts和Frames来能基本上优化了不足的地方,然后我们可以根据TraceView来分析具体函数花了多长时间来进一步优化代码提高性能。
上面介绍了Systrace中不同区域的功能,当然最有趣的还是Alerts和Frames两栏,让我们来选择最上方的Alerts瞧瞧:
这个警告指出了,有一个View#draw()方法执行了比较长的时间。我们可以在下面看到问题的描述、链接,甚至是相关的视频。
下面我们看Frames这一行,可以看到这里展示了被绘制出来的每一帧,并且用绿、黄、红三颜色来区分它们在绘制时的性能。我们选一个红色帧来瞅瞅:
在最下方,我们看到了与这一帧所相关的一些警告。
在这三个警告中,有一个是我们上面所提到的(View#draw())。接下来我们在这一帧处放大并在下方展开“Inflation during ListView recycling”这条警告:
我们可以看到警告部分的总耗时32毫秒,远高于了我们对保障60fps所需的16.6毫秒绘制时间。同时还有更多的ListView每个条目的绘制时间,大约是6毫秒每个条目,总共五个。而Description描述项中的内容会帮助我们理解问题,甚至提供问题的解决方案。回到我们上一张图片,我们可以在“inflate”这一个块区处放大,并且观察到底是哪些View在被填充过程中耗时比较严重。
下面是另外一个渲染过慢的实例:
在选择了某一帧之后,我们可以按“m”键来高亮这一帧,并且在上方看到了这一部分的耗时,如图,我们看到了这一阵的绘制总共耗时大约19毫秒。而当我们展开这一帧唯一的一个警告时,我们发现了“Scheduling delay”这条错误。
Scheduling delay(调度延迟)的意思就是一个线程在处理一块运算的时候,在很长一段时间都没有被分配到CPU上面做运算,从而导致这个线程在很长一段时间都没有完成工作。我们选择这一帧中最长的一块,从而得到更加详细的信息:
在红框区域内,我们看到了“Wall duration”,他代表着这一区块的开始到结束的耗时。之所以叫作“Wall duration”,是因为他就像是墙上的一个时钟,从线程的一开始就为你计时。
而CPU Duration一项中显示了实际CPU在处理这一区块所消耗的时间。
很显然,两个时间的差距还是非常大的。整个区块耗时18毫秒,而在这之中CPU只消耗了4毫秒的时间去运算。这就有点奇怪了,所以我们应该看一下在这整个过程之中,CPU去干吗了。
可以看到,所有四个线程都非常的繁忙。
选择其中的一个线程会告诉我们是哪个程序在占用他,在这里是一个包名为com.udinic.keepbusyapp的程序。在这里,由于另外一个程序占用CPU,导致了我们的程序未能获得足够的CPU资源。
但是这种情况其实是暂时的,因为被其他后台应用占用CPU的情况并不多见,但仍有其他应用的线程或是主线程占用CPU。而Systrace也只能为我们提供一个概览,他的深度是有限的。所以要找到我们app中到底是什么让我们的CPU繁忙,我们还要借助另一个工具——Traceview。
通过Trace类跟踪应用案例
Trace类能够让你在任何时候跟踪应用的一举一动。在你获取trace的过程中,Trace.beginSection()与Trace.endSection()之间代码工作会一直被追踪。
比如,我们在recyclerview的bindview中使用sleep模拟耗时操作,滚动时会出现明显卡顿
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onBindViewHolder(StaggredAdapter.MyHolder holder, int position) {
holder.mivItem.setImageResource(mlstPics.get(position));
Trace.beginSection("aaa");//生成的trace文件中,会在跟踪的代码段执行对应时间轴区间打上一个tag标记
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Trace.endSection();//Trace的begin与end必须在同一线程之中执行
}
x
1
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
2
@Override
3
public void onBindViewHolder(StaggredAdapter.MyHolder holder, int position) {
4
holder.mivItem.setImageResource(mlstPics.get(position));
5
6
Trace.beginSection("aaa");//生成的trace文件中,会在跟踪的代码段执行对应时间轴区间打上一个tag标记
7
try {
8
Thread.sleep(1000);
9
} catch (InterruptedException e) {
10
e.printStackTrace();
11
}
12
Trace.endSection();//Trace的begin与end必须在同一线程之中执行
13
}
使用systrace跟踪,生成如下分析图
找到分析进程对应的ui线程,线程的纵向信息记录的是调用关系,横向信息记录的是调用顺序,代码中的标记“aaa”标记了sleep的时间段,可以按m键高亮显示这个标记对应的时间段,可以看出以下信息:
- 丢帧,在aaa标记时段的前面,有一帧标红的frame,它跟下一个frame之间间隔事件很长(代码sleep中)
- 调用关系,RV滚动 ---> bindview ---> aaa(sleep)
2017-10-20