一、抓Trace的网址
1. 4个抓trace的网址
(1) chrome://tracing/ (2) https://ui.perfetto.dev/v18.0-d996f9e27/assets/catapult_trace_viewer.html (3) https://ui.perfetto.dev/#!/ (4) https://ui.perfetto.dev/#!/viewer
1和2的区别是2能打开更多的trace,但是点击 Process<pid> 栏里的第一级trace显示的是相对时间,但是1显示的是绝对时间,这个绝对时间是可以和内核Log前面的时间是可以对应起来的。
2. systrace原始工具抓trace的方法:
# cd /sdk/platform-tools/systrace # python systrace.py [options][category1][category2]...[categoryN] options 描述 -o <FILE> 输出的目标文件 -t N, -time=N 执行时间,默认是5s -k <KFUNCS>, -ktrace=<KFUNCS> 追踪kernel函数,用逗号分割 -a <APP_NAMW>, -app <APP_NAMW> 追踪应用包名,用逗号分割 --from-file=<FROM_FILE> 从文件中创建互动的systrace -e <DEVICE_SERIAL>, --serial=<DEVICE_SERIAL> 指定设备 -l, --list-categories 列举可用的tags
3. perfetto对应的仓库为:source/{vnd/sys}/external/perfetto 可以对显示格式进行定制化。
二、sched_waking & sched_wakeup
1. InputDispatcher选核案例
(1) 在旧的解析网址中 InputDispatcher 线程那里找到绝对时间,然后以.txt格式打开trace,找到案发现场:
InputReader 1964-1964 ( 1576) [004] d..5 72113.123884: sched_waking: comm=InputDispatcher pid=1963 prio=112 target_cpu=006 InputReader 1964-1964 ( 1576) [004] d..6 72113.123904: sched_wakeup: comm=InputDispatcher pid=1963 prio=112 target_cpu=006 OAdOutputTh 3908-3908 ( 884) [004] d..2 72113.132359: sched_switch: prev_comm=OAdOutputTh prev_pid=3908 prev_prio=101 prev_state=S ==> next_comm=InputDispatcher next_pid=1963 next_prio=112
(2) 说明:在CPU4上被别的线程唤醒的,就显示在CPU4唤醒,但是并不是目标CPU选择在CPU4上了。
(3) 老版Trace上解析这段runnable是8.455ms,而新版trace解析是8.475ms。说明旧版的Trace解析网址计算的runnable时间是:sched_switch - sched_wakeup 的时间,新版本的trace解析网址是 sched_switch - sched_waking 的时间。
72113.13236 - 72113.12388 = 8.475ms 72113.13236 - 72113.12390 = 8.455ms
(4) trace详细解读
//这是 try_to_wake_up 函数中为任务选核之前打印的trace,含义为在CPU4上唤醒,被唤醒的名字、pid、优先级等,任务之前是运行在CPU6上的 InputReader 1964-1964 ( 1576) [004] d..5 72113.123884: sched_waking: comm=InputDispatcher pid=1963 prio=112 target_cpu=006 //这是 try_to_wake_up 函数中为任务选核之后打印的trace,在CPU4上继续执行唤醒流程,唤醒后选核又选在了CPU6上 InputReader 1964-1964 ( 1576) [004] d..6 72113.123904: sched_wakeup: comm=InputDispatcher pid=1963 prio=112 target_cpu=006 //又运行在CPU4上(CPU6上无法得到运行,因为都是RT线程干不过) OAdOutputTh 3908-3908 ( 884) [004] d..2 72113.132359: sched_switch: prev_comm=OAdOutputTh prev_pid=3908 prev_prio=101 prev_state=S ==> next_comm=InputDispatcher next_pid=1963 next_prio=112
(5) 代码分析
try_to_wake_up //core.c trace_sched_waking(p); //这个时候还没有选核,trace打印的cpu还是任务上次运行的cpu. p->state = TASK_WAKING; cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); //唤醒选核 if (task_cpu(p) != cpu) { //选的核不是之前运行的CPU,要迁移 ... set_task_cpu(p, cpu); //这里将 p->cpu 指向新选出的cpu } ttwu_queue(p, cpu, wake_flags); ttwu_do_activate(rq, p, wake_flags, &rf); activate_task(rq, p, en_flags); //将任务放到新选出的cpu的rq上 ttwu_do_wakeup(rq, p, wake_flags, rf); check_preempt_curr(rq, p, wake_flags); //触发一次抢占 p->state = TASK_RUNNING; //虽然设置为running,但是此时还是runnable状态的 trace_sched_wakeup(p); //此时打印的就是选核的目标CPU了。
三、杂项
1. 区分前后台
检索 OomAdjuster, 这一列显示的"setProcessGroup com.tencent.mm to 5 " 5就是TOP-APP,就是前台。上层TOP-APP定义在 android/frameworks/base/core/java/android/os/Process.java:public static final int THREAD_GROUP_TOP_APP = 5;
kernel中的TOP-APP分组柳杰定义的是4,定义为#define SA_CGROUP_TOP_APP 4,其打印发现的,MTK和Qcom都是。
第二个区分前后台的方法是在CPU Info中看其优先级,一般应用在前台优先级会设置为110,在后台设置为120。
/dev/cpuset/top-app # echo <PID> > cgroup.procs 上层设置为前台的方法应该是这样的,echo进去几个就有几个在前台。可以使用新解析工具打开trace,然后在CpuInfo信息中检索OomAdjuster,可同时看前台应用主线程的prio120->110的切换时间来对照着看。
2. sched_switch 中的R+中的加号表示传参preempt=1,preempt参数来自:
__schedule(bool preempt) trace_sched_switch(preempt, prev, next); //在trace打印prev_state=R+中的这个R+的加号表示传参preempt=1 sched_switch: prev_comm=kswapd0:0 prev_pid=171 prev_prio=120 prev_state=R+ ==> next_comm=kworker/5:1 next_pid=21307 next_prio=120
3. 下面两个作用相同,但使用场景却不同,前者设置方便,但是后者更适合于在抓trace前添加trace_event的时候使用,因为前者echo后可能覆盖,而后者不会。
/sys/kernel/tracing # echo sched_switch > set_event /sys/kernel/tracing # echo 1 > ./events/sched/sched_switch/enable
4. Perfetto trace上Ctrl+F检索startup,可以看到"Android App Startups"条目,可以看到启动的app,可以用来区分是启动动画还是退出动画。
5. 可以参考 process_one_work() 来实现对异步函数的trace的解析和显示
process_one_work() trace_workqueue_execute_start(work); worker->current_func(work); //被包裹着 trace_workqueue_execute_end(work); kworker/u24:3-13447 ( 13447) [000] .... 714031.881139: workqueue_execute_start: work struct 0000000025ceb863: function __typeid__ZTSFvP11work_structE_global_addr ... kworker/u24:3-13447 ( 13447) [000] .... 714031.884821: workqueue_execute_end: work struct 0000000025ceb863 //以.txt格式打开.html的trace,检索"work struct"得到如下格式控制,应该需要满足格式才行: const workqueueExecuteStartRE=/work struct (.+): function (\S+)/;const workqueueExecuteEndRE=/work struct (.+)/;
6. 可以通过"{"和"}"快捷键实现对binder和async binder的跳转。
本文参考链接:https://www.cnblogs.com/hellokitty2/p/16025836.html