上图基本就能说清systrace的整个框架:
- 1、systrace调用atrace抓取目标机的trace数据;
- 2、systrace把trace数据和’prefix.html’、’suffix.html’、’systrace_trace_viewer.html’合成一个’trace.html’文件;
- 3、使用chrome浏览器打开’trace.html’就可以非常方便的以图形化的形式来查看和分析trace数据。背后是Trace-Viewer的脚本在运行;
内核态和用户态的存储trace数据的实现:
- 1、内核trace信息,通过trace event记录到ftrace的buffer中;
- 2、用户态(app/java framework/native)是通过使用Trace类来记录trace信息的,也是记录到内核ftrace buffer当中的,是通过”/sys/kernel/debug/tracing/trace_marker”接口记录的。
1、systrace的使用
在定位Android性能问题的时候,我们经常会用到systrace工具。配置到adb,连接上目标手机,在主机侧使用类似命令启动systrace:
1 | ASOP_ROOT/external/chromium-trace/catapult/systrace/bin$ ./systrace -o trace.html -t 10 gfx view wm am dalvik input sched freq idle |
systrace命令在ASOP源码包的ASOP_ROOT/external/chromium-trace/catapult/systrace/bin路径下。上述命令的参数:
- ‘-t 10’,指定了抓取trace的时长为10s;
- ‘-o trace.html’,指定了trace的输出文件;
- ‘gfx view wm am dalvik input sched freq idle’,指定了需要抓取的事件;
在命令启动以后,我们就可以在目标机上进行滑动、启动app等一系列操作,10s内的这些操作都会被记录下来最后dump进trace.html文件。我们可以通过google的chrome浏览器来查看、分析trace.html:’google-chrome trace.html’。
可以通过’-l’选项来查看目标机支持的systrace事件全集:
1 | $ ./systrace -l |
还可以通过’–help’选项来查看systrace命令的详细选项:
1 | $ ./systrace --help |
2、主机systrace命令的实现(python)
systrace实质上是一个python文件,整个python相关包在ASOP_ROOT/external/chromium-trace/catapult/路径下,以此路径为根(root=ASOP_ROOT/external/chromium-trace/catapult),我们来分析它的实现过程:
./systrace/bin/systrace:
1 | import os |
↓
./systrace/systrace/run_systrace.py:
1 |
|
我们可以看到trace过的重点最后落在SystraceRunner对象的创建,以及.StartTracing()/.StopTracing()/.OutputSystraceResults()几个方法上。
下面我们详细分析一下这几个步骤的实现过程。
2.1、SystraceRunner类的初始化
SystraceRunner类在systrace_runner.py文件当中,我们来查看起具体实现。
./systrace/systrace/systrace_runner.py:
1 | AGENT_MODULES = [android_process_data_agent, atrace_agent, |
- 1、分析tracing_controller.CreateAgentsWithConfig()的实现;
./systrace/systrace/tracing_controller.py:
1 | def CreateAgentsWithConfig(options, modules): |
|→
./systrace/systrace/tracing_agents/atrace_agent.py:
1 | # (1.1.1) 创建一个AtraceConfig类的对象,用来存储optios中的相关配置 # |
1 | # (1.1.2) 创建一个AtraceAgent类的对象 # |
|→
./systrace/systrace/tracing_agents/android_process_data_agent.py:
1 | def try_create_agent(config): |
- 2、分析tracing_controller.GetControllerConfig()的实现;
./systrace/systrace/tracing_controller.py:
1 | # (2.1) 创建一个TracingControllerConfig类的对象,用来存储optios中的相关配置 # |
- 3、分析tracing_controller.TracingController()的实现;
./systrace/systrace/tracing_controller.py:
1 | # (3.1) 创建一个TracingController类的对象,稍后的start/stop/output操作都使用该对象的方法 # |
2.2、SystraceRunner.StartTracing()
入口是SystraceRunner类的.StartTracing()方法,./systrace/systrace/systrace_runner.py:
1 | class SystraceRunner(object): |
↓
继续调用到TracingController类的.StartTracing()方法,./systrace/systrace/tracing_controller.py:
1 | class TracingController(object): |
|→
继续调用到AtraceAgent类的.StartTracing()方法,./systrace/systrace/tracing_agents/atrace_agent.py:
1 | class AtraceAgent(tracing_agents.TracingAgent): |
|→
继续调用到AndroidProcessDataAgent类的.StartTracing()方法,./systrace/systrace/tracing_agents/android_process_data_agent.py:
1 | class AndroidProcessDataAgent(tracing_agents.TracingAgent): |
2.3、SystraceRunner.StopTracing()
入口是SystraceRunner类的.StopTracing()方法,./systrace/systrace/systrace_runner.py:
1 | class SystraceRunner(object): |
↓
继续调用到TracingController类的.StopTracing()方法,./systrace/systrace/tracing_controller.py:
1 | class TracingController(object): |
|→
继续调用到AtraceAgent类的.StopAgentTracing()方法,./systrace/systrace/tracing_agents/atrace_agent.py:
1 | class AtraceAgent(tracing_agents.TracingAgent): |
|→
继续调用到AndroidProcessDataAgent类的.StopAgentTracing()方法,./systrace/systrace/tracing_agents/android_process_data_agent.py:
1 | class AndroidProcessDataAgent(tracing_agents.TracingAgent): |
2.4、SystraceRunner.OutputSystraceResults()
入口是SystraceRunner类的.OutputSystraceResults()方法,./systrace/systrace/systrace_runner.py:
1 | class SystraceRunner(object): |
↓
继续调用到output_generator模块中的GenerateHTMLOutput()函数,./systrace/systrace/output_generator.py:
1 | def GenerateHTMLOutput(trace_results, output_file_name): |
2.4、执行的命令
我们在devil/devil/android/device_utils.py的RunShellCommand.run()和devil/devil/utils/cmd_helper.py的Popen()处加上调试打印,看到’./systrace -o trace.html -t 10 gfx view wm am dalvik input sched freq idle‘命令的具体执行过程如下:
1 | ASOP_ROOT/external/chromium-trace/catapult/systrace/bin/systrace -o trace.html -t 10 gfx view wm am dalvik input sched freq idle |
精简起来是调用目标机上的atrace命令实现的:
1 | adb shell atrace -z -t 10 -b 4096 gfx view wm am dalvik input sched freq idle --async_start |
3、目标机atrace命令的实现(c++)
从上一节可以看到主机的systrace命令实际最后都是调用目标机上的atrace命令实现的。从atrace的命令格式上看,分为两部分:
- [options]:就是’-‘和’–’打头的选项;
- [categories…]:指定需要trace哪些事件。就是类似’gfx view wm am dalvik input sched freq idle’这些;
1 | $ atrace --help |
atrace的源码在ASOP_ROOT/frameworks/native/cmds/atrace/atrace.cpp,我们来具体分析一下它的实现:
1 | int main(int argc, char **argv) |
3.1、categories
categories指的是可被trace的事件的类别,它的全集如下:
1 | /* Tracing categories */ |
这些categories可以简单分为3类:
- 1、使用内核ftrace的trace event实现的内核事件。例如”sched”,.sysfiles字段指定了trace event的路径;
- 2、使用Trace类实现的用户态事件(app/java framework/native)。例如”input”,.tags字段指定了trace tag;
- 3、service类型的事件。如k_coreServiceCategory和k_pdxServiceCategory;
具体目标机可能只支持categories全集中的一部分,所以atrace会对命令参数中指定的categories本机是否支持进行判断:
1 | main() -> setCategoryEnable(): |
我们也可以使用atrace命令的”–list_categories”选项,列出当前目标机支持的所有categories:
1 | main() -> listSupportedCategories(): |
3.2、’–async_start’
当atrace命令使用了’–async_start’选项以后,会启动指定的所有categories:
1 | static bool setUpTrace() |
1 | static bool startTrace() |
3.3、’–async_dump’
当atrace命令使用了’–async_dump’选项以后,会dump出trace内容:
1 | main() |
3.4、’–async_stop’
当atrace命令使用了’–async_stop’选项以后,会停止所有categories的trace信息抓取:
1 | static void stopTrace() |
4、目标机Trace类的使用(java/c++)
atrace只是一个控制命令,实际的trace数据路径是这样分工的:
- 1、内核trace信息,通过trace event记录到ftrace的buffer中;
- 2、用户态(app/java framework/native)是通过使用Trace类来记录trace信息的,实际上是通过”/sys/kernel/debug/tracing/trace_marker”接口记录到内核ftracebuffer当中的。
我们查看atrace dump出来的原始信息,可以看到对应的”tracing_mark_write”字段:
1 | ndroid.systemui-1856 ( 1856) [001] ...1 89888.553074: tracing_mark_write: S|1856|animator:alpha|62928891 |
这样内核态和用户态的trace数据最后都写入到了ftrace buffer当中,也实现了时间同步,最后通过”/sys/kernel/debug/tracing/trace”接口读出。
在用户态的各个层次是这样使用Trace类的:
4.1、app
1 | import android.os.Trace; |
详细例子可以参考:systrace
1 | public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { |
然后通过python systrace.py –app=sectionName 指定apk,或者通过ddms选择指定apk,抓取systrace分析。
4.2、Java framework
1 | import android.os.Trace; |
4.3、Native framework
1 | #include <cutils/trace.h> |
5、主机Trace-Viewer的实现(js)
从systrace python文件的解析我们可以看到,最后把trace数据和’prefix.html’、’suffix.html’、’systrace_trace_viewer.html’合成一个’trace.html’文件,使用chrome浏览器打开’trace.html’就可以非常方便的以图形化的形式来查看和分析trace数据。
这里面的关键就是Trace-Viewer,核心是其中的js脚本。该项目的github主页。
本人js功底太差,看了一堆资料还是不得要领。如有感兴趣的同学可以继续深入研究,Trace-Viewer功能非常强大,用来做trace数据的分析和实现再合适不过了,如果可以利用起来自己修改非常强大。
一些参考文档:
catapult on github
Trace-Viewer
Trace Event Format
extending-and-customizing-trace-viewer
Trace Viewer getting-started
Trace Viewer doc
标签
和 Polymer 一起加入 Web 组件革命
The Trace Event Profiling Tool (about:tracing)
Recording Tracing Runs
Understanding about:tracing results
FrameViewer How-To
这里面最有名的一个问题是在trace.html中加上一条自定义的trace曲线,主要步骤如下:
- 1、在内核中实现自己的trace event;
- 2、在atrace.cpp代码的k_categories[]数组中加上对应的category;
- 3、在Trace-Viewer中加上对这类category的解析,Trace-Viewer对数据的解析都是通过parser来实现的,可以仿照linux_perf中的实现增加一条parser,重新生成’systrace_trace_viewer.html’、’trace.html’文件即可。