systrace跟踪进程切换原理

问题: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);
	}
}