Systrace 和 Trace:
Trace 报告提供了 Android 系统进程在特定时间段内的整体情况。 它检查捕获的跟踪信息,并突出显示其检查到的问题,例如在显示运动或动画时UI粗糙,并提供关于如何解决这些问题的建议。
但是,Systrace 不会在应用程序进程中收集有关代码执行的信息。 有关您的应用程序执行哪些方法以及使用多少 CPU 资源的更多详细信息,请使用Android Studio的内置CPU分析器(Android Profiler)。
准备工作
- 下载并安装 Android SDK Tools
- 安装 Python ,并将其包含在工作站的执行路径中。
- 连接手机,打开开发者选项中的 USB Debug 选项 。
- Systrace 存储路径如下 android-sdk/platform-tools/systrace/
如果你要运行Systrace,Android SDK Tools 20 以上的版本。
同时,现在Systrace只支持Android4.1以上的版本。
第一步:
首先,在手机端准备好你需要分析的过程的环境;比如假设你要分析 App 的冷启动过程,那就先把 App 进程杀掉,切换到 Launcher 中有你的 App 图标的那个页面,随时准备点击图标启动进程;假设你要分析某个 Activity 的卡顿情况,那就先在手机上进入到上一个 Activity ,随时准备点按钮切换到待分析的 Activity 中。
第二步:Python 启动
手机上 App 上的环境准备好以后,打开 PC 端的命令行;进入 systrace 的目录,也即(假设 $ANDROID_HOME 是你 Android SDK 的根目录):
cd $ANDROID_HOME/platform-tools/systrace
然后执行如下命令:
./systrace.py -t 10 sched gfx view wm am app webview -a <package-name>
这样,systrace.py
这个脚本就通过adb给手机发送了收集trace的通知;与此同时,切换到手机上进行你需要分析的操作,比如点击Launcher中App的Icon启动App,或者进入某个Activity开始滑动ListView/RecyclerView。经过你指定的时间之后(以上是10s),就会有trace数据生成在当前目录,默认是 trace.html
;用Chrome浏览器打开即可。
浏览 Systrace 报告
Systrace 会生成包含多个部分的输出 HTML 文件。该报告列出了每个进程的线程。如果给定线程会渲染界面帧,该报告还会沿时间轴指明所渲染的帧。当您在报告中从左向右移动时,时间会向前推移。
报告从上到下包含以下几个部分:
用户互动
第一部分包含表示应用或游戏中的具体用户互动(例如点按设备屏幕)的条形图。这些互动可用作有用的时间标记。
CPU 活动
下一部分显示了表示每个 CPU 中的线程活动的条形图。这些条形会显示所有应用(包括您的应用或游戏)中的 CPU 活动。
CPU 活动部分可以展开,展开后您就可以查看每个 CPU 的时钟频率。图 1 展示了一个收起后的 CPU 活动部分示例,图 2 展示了显示时钟频率的展开后版本:
图 1. Systrace 报告中的 CPU 活动示例(收起后的视图) 图 2. Systrace 报告中显示 CPU 时钟频率的 CPU 活动示例(展开后的视图)系统事件
此部分中的直方图会显示特定的系统级事件,例如特定对象的纹理计数和总大小。
值得仔细检查的直方图是标记为 SurfaceView 的直方图。计数表示已传递到显示管道并等待显示在设备屏幕上的组合帧缓冲区的数量。由于大多数设备都会进行双重或三重缓冲,因此该计数几乎总为 0、1 或 2。
描绘 Surface Flinger 进程(包括 VSync 事件和界面线程交换工作)的其他直方图,如图 3 所示:
图 3. Systrace 报告中的 Surface Flinger 图表示例显示帧
这一部分通常是报告中最顶部的部分,描绘了一条多色线条,后面是成堆的条形。这些形状表示已创建的特定线程的状态和帧堆栈。堆栈的每个层级代表对 beginSection()
的调用,或您为应用或游戏定义的自定义跟踪事件的开头。
注意:界面线程(即通常运行您的应用或游戏的主线程)始终会显示为第一个线程。
每个条形堆上方的多色线条表示特定线程随时间变化的一组状态。每段线条可以包含以下颜色之一:
绿色:正在运行
线程正在完成与某个进程相关的工作或正在响应中断。
蓝色:可运行
线程可以运行但目前未进行调度。
白色:休眠
线程没有可执行的任务,可能是因为线程在遇到斥锁定时被阻止。
橙色:不可中断的休眠
线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成。
紫色:可中断的休眠
线程在遇到另一项内核操作(通常是内存管理)时被阻止。
注意:在 Systrace 报告中,您可以点击该线条以确定该线程在给定时间由哪个 CPU 控制。
从framework层面出发:
键盘快捷键:
下表列出了查看 Systrace 报告时可以使用的键盘快捷键:
调查性能问题
以下各部分介绍了如何检查报告中的信息以查找和修复性能问题。
识别性能问题
浏览 Systrace 报告时,您可以通过执行以下一项或多项操作来更轻松地识别性能问题:
- 通过在时间间隔周围绘制一个矩形来选择所需的时间间隔。
- 使用标尺工具标记或突出显示问题区域。
- 依次点击 View Options > Highlight VSync,以显示每项显示屏刷新操作。
检查界面帧和提醒
如图 4 所示,Systrace 报告列出了渲染界面帧的每个进程,并指明了沿时间轴渲染的每个帧。在 16.6 毫秒内渲染的必须保持每秒 60 帧稳定帧速率的帧会以绿色圆圈表示。渲染时间超过 16.6 毫秒的帧会以黄色或红色圆圈表示。
图 4. 放大长时间运行的帧后的 Systrace 显示界面注意:在搭载 Android 5.0(API 级别 21)或更高版本的设备上,渲染帧的工作拆分为界面线程和渲染线程。在以前的版本中,创建帧的所有工作都是在界面线程中完成的。
点击某个帧圆圈可将其突出显示,并提供有关系统为渲染该帧所做工作的其他信息,包括提醒。此报告还会显示系统在渲染该帧时执行的方法。您可以调查这些方法以确定界面卡顿的可能原因。
图 5. 选择有问题的帧后,跟踪报告下方会显示一条提醒,用于指明问题所在选择运行速度慢的帧后,您可能会在报告的底部窗格中看到一条提醒。图 5 中显示的提醒会指明帧的主要问题是在 ListView
回收和重新绑定上花费了太多时间。指向跟踪记录中相关事件的链接可详细说明系统在此期间执行的操作。
要查看此工具在您的跟踪记录中发现的每条提醒以及设备触发每条提醒的次数,请点击窗口最右侧的 Alerts 标签,如图 6 所示。Alerts 面板可帮助您了解跟踪记录中出现的问题以及这些问题导致出现卡顿的频率。您可以将此面板视为要修正的错误列表。通常情况下,只需对一个区域进行细微改动或改进即可移除整组提醒。
图 6. 点击 Alert 按钮可显示提醒标签如果您发现在界面线程上执行的工作太多,请使用以下方法之一来帮助确定哪些方法占用了过多的 CPU 时间:
- 如果您想了解哪些方法可能会导致瓶颈,请在这些方法中添加跟踪标记。要了解详情,请参阅有关如何在代码中定义自定义事件的指南。
- 如果您不确定界面瓶颈的来源,请使用 Android Studio 中提供的
CPU Profiler
您可以生成跟踪日志,然后使用 CPU Profiler 导入和检查这些日志。
为 Systrace 定义自定义事件
Systrace 仅会在系统级别显示进程的相关信息,因此有时很难知道应用或游戏的哪些方法是在给定时间相对于系统事件执行的。
Android 平台提供了一个跟踪 API,可用于为特定的代码段添加标签。如果您捕获应用的“调试”版本的新系统跟踪并添加 -a
选项(如以下代码段所示),这些自定义事件便会显示在 Systrace 报告中:
python systrace.py -a com.example.myapp -b 16384 \
-o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal \
dalvik camera input res
托管代码
在 Android 4.3(API 级别 18)及更高版本中,您可以使用代码中的 Trace
类来定义随后出现在 Systrace 报告中的自定义事件,如以下代码段所示。
注意:当您调用 beginSection() 多次时,调用 endSection() 只会结束最近调用的那个 beginSection() 方法。因此,对于嵌套调用(如以下代码段中的调用),请务必将每次对 beginSection() 的调用与对 endSection() 的调用正确匹配。
此外,您不能在一个线程上调用 beginSection(),而从另一个线程将其结束;您必须在同一个线程中调用这两个方法。
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 and catch statements, always call "endSection()" in a
// "finally" block. That way, the method is invoked even when an
// exception occurs.
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();
}
}
}
语法:
systrace.py
命令的一般用法是:
python systrace.py -t 10 [other-options] [categories]
其中,[options]
是一些命令参数,[category]
等是你感兴趣的系统模块,比如view代表view系统(包含绘制流程),am代表ActivityManager(包含Activity创建过程等);分析不同问题的时候,可以选择不同你感兴趣的模块。需要重复的是,尽可能缩小需要Trace的模块,其一是数据量小易与分析;其二,虽然systrace本身开销很小,但是缩小需要Trace的模块也能减少运行时开销。比如你分析卡顿的时候,power
, webview
就几乎是无用的。
[option]
中比较重要的几个参数如下:
- -a <package_name>:这个选项可以开启指定包名App中自定义Trace Label的Trace功能。也就是说,如果你在代码中使用了
Trace.beginSection("tag")
,Trace.endSection
;默认情况下,你的这些代码是不会生效的,因此,这个选项一定要开启! - -t N:用来指定Trace运行的时间,取决于你需要分析过程的时间;还是那句话,在需要的时候尽可能缩小时间;当然,绝对不要把时间设的太短导致你操作没完Trace就跑完了,这样会出现
Did not finish
的标签,分析数据就基本无效了。 - -l:这个用来列出你分析的那个手机系统支持的Trace模块;也就是上面命令中
[category1]
能使用的部分;不同版本的系统能支持的模块是不同的,一般来说,高版本的支持的模块更多。 - -o FILE:指定trace数据文件的输出路径,如果不指定就是当前目录的
trace.html
。
systrace.py -l
可以输出手机能支持的Trace模块,而且输出还给出了此模块的用途;常用的模块如下:
-
sched
: CPU调度的信息,非常重要;你能看到CPU在每个时间段在运行什么线程;线程调度情况,比如锁信息。 -
gfx
:Graphic系统的相关信息,包括SerfaceFlinger,VSYNC消息,Texture,RenderThread等;分析卡顿非常依赖这个。 -
view
: View绘制系统的相关信息,比如onMeasure,onLayout等;对分析卡顿比较有帮助。 -
am
:ActivityManager调用的相关信息;用来分析Activity的启动过程比较有效。 -
dalvik
: 虚拟机相关信息,比如GC停顿等。 -
binder_driver
: Binder驱动的相关信息,如果你怀疑是Binder IPC的问题,不妨打开这个。 -
core_services
: SystemServer中系统核心Service的相关信息,分析特定问题用。
总结:
如果不指定任何类别或选项,systrace将生成包含所有可用类别的报告,并使用默认设置。
Alerts面板可帮助查看发生了哪些问题,以及发生的频率。 将Alert面板看作是要修复的错误列表。
android.bg是一个framework层提供的一个工具类,方便在使用的时候来创建一个异步的线程,拥有独立的handler,在应用启动过程中AMS也会使用这个工具类.
Main Thread 和Render Thread 都各自维护了一份应用程序窗口视图信息。各自维护了一份应用程序窗口视图信息的目的,就是为了可以互不干扰,进而实现最大程度的并行。其中,Render Thread维护的应用程序窗口视图信息是来自于 Main Thread 的。因此,当Main Thread 维护的应用程序窗口信息发生了变化时,就需要同步到 Render Thread 去。
多次调用beginSection(String)时,调用endSection()只会结束最近调用的beginSection(String)方法。 因此,对于嵌套的调用,例如上面示例中的调用,您需要确保通过调用endSection()将每个调用正确匹配到beginSection()。
不能在一个线程上调用beginSection()并从另一个线程结束 - 您必须从同一线程调用endSection()。
申明开始结束的图片来源网络,侵删
网友评论