问题:systrace上这些线程切换是怎么盯梢的?
systrace、atrace、perfetto 他们之间的联系:
-
systrace 根据Android手机上的可执行文件 atrace
- 本质上便是个 atrace 数据的可视化工具
- 一起经过封装atrace指令,集成了一些python工具
-
atrace 又根据 ftrace
- atrace 直接运用了 ftrace 的 sched_switch 盯梢器,用于盯梢进程调度信息。
- 一起 atrace 相当于对ftrace做了扩展
- atrace 在Android源码的一些关键地方插桩打点,生成了各种数据。比如SurfaceFlinger、view绘制体系的关键函数。
- 并运用了ftrace的内核缓冲区保存这些数据,运用的 ftrace 的底层架构,时刻体系等等。
-
perfetto 也能够说是根据 atrace 和 ftrace
- perfetto 的数据源虽然不限于 atrace 和 ftrace,但其主要功能是这俩数据源提供的
也便是说 systrace、atrace、perfetto 这些工具的进程调度信息都来源于 ftrace 的 sched_switch 盯梢器。
ftrace 盯梢器
ftrace 当时包含多个盯梢器,用于盯梢不同类型的信息,比如进程调度、中止封闭等。能够检查文件 available_tracers 获取内核当时支持的盯梢器列表。在编译内核时,也能够看到内核支持的盯梢器对应的选项。
- nop盯梢器不会盯梢任何内核活动,将 nop 写入 current_tracer 文件能够删去之前所运用的盯梢器,并清空之前收集到的盯梢信息,即刷新 trace 文件。
- function盯梢器能够盯梢内核函数的执行情况;能够经过文件 set_ftrace_filter 显现指定要盯梢的函数。
- function_graph盯梢器能够显现类似 C 源码的函数调用联系图,这样检查起来比较直观一些;能够经过文件 set_grapch_function 显现指定要生成调用流程图的函数。
- sched_switch盯梢器能够对内核中的进程调度活动进行盯梢。
- irqsoff盯梢器和 preemptoff 盯梢器别离盯梢封闭中止的代码和制止进程抢占的代码,并记载封闭的最大时长,preemptirqsoff盯梢器则能够看做它们的组合
关于ftrace盯梢调用栈其原理是:mcount
- 本质上是一种静态代码插装技术,即在每一个函数入口处经过编译器选项,自动刺进对mcount的调用:call_mcount
关于ftrace盯梢进程调度原理是:tracepoint
- 静态编译进内核,提供一个钩子函数,当tracepoint翻开时,会去调用probe函数,经过定义并注册这个probe函数来实现咱们的trace功能
关于ftrace具体的原理,这儿讲的比较具体:
wenku.baidu.com/view/fa90b1…
盯梢进程调度原理本质:插桩
便是这么朴实无华!
只不过 ftrace 插桩做的高级一些。
高情商的说:静态探针。
首要进程调度(准去的说是线程task调度)的时候,会调用内核的 schedule 函数:
这个函数会让出CPU,并寻觅下一个调度的task,一起切换task的上下文(寄存器的堆栈指针等等)。
关键就在 schedule 源码中。
schedule 源码中打点,调用了 trace_sched_switch
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/kernel/sched/core.c;l=6710?q=trace_sched_switch&ss=android%2Fkernel%2Fsuperproject
// common/kernel/sched/core.c
static void __sched notrace __schedule(unsigned int sched_mode)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
unsigned long prev_state;
struct rq_flags rf;
struct rq *rq;
int cpu;
cpu = smp_processor_id();
rq = cpu_rq(cpu);
prev = rq->curr;
//....................
if (likely(prev != next)) {
rq->nr_switches++;
/*
* RCU users of rcu_dereference(rq->curr) may not see
* changes to task_struct made by pick_next_task().
*/
RCU_INIT_POINTER(rq->curr, next);
/*
* The membarrier system call requires each architecture
* to have a full memory barrier after updating
* rq->curr, before returning to user-space.
*
* Here are the schemes providing that barrier on the
* various architectures:
* - mm ? switch_mm() : mmdrop() for x86, s390, sparc, PowerPC.
* switch_mm() rely on membarrier_arch_switch_mm() on PowerPC.
* - finish_lock_switch() for weakly-ordered
* architectures where spin_unlock is a full barrier,
* - switch_to() for arm64 (weakly-ordered, spin_unlock
* is a RELEASE barrier),
*/
++*switch_count;
migrate_disable_switch(rq, prev);
// PSI内核打点代码,PSI功能计算的代码也是这么的朴实无华!
psi_sched_switch(prev, next, !task_on_rq_queued(prev));
//=============== ftrace打点代码 ========================
trace_sched_switch(sched_mode & SM_MASK_PREEMPT, prev, next, prev_state);
/* Also unlocks the rq: */
rq = context_switch(rq, prev, next, &rf);
} else {
rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
rq_unpin_lock(rq, &rf);
__balance_callbacks(rq);
raw_spin_rq_unlock_irq(rq);
}
}