函数被压入堆栈并在函数调用返回时弹出。我想知道这个推送和弹出实际发生在内核代码的哪个位置。
它不会发生在内核代码中,它是由处理器完成的。 IE。当 x86 汇编 CPU 找到 call 指令时,它会将 IP 推入堆栈,而 ret 指令将弹出该值。
您可以使用call my_tracing_routine 修补内核中的每个call 和ret 指令,并在那里记录指令指针,而不是将控制权传递给原始被调用者/调用者。有一些工具可以做到这一点:LTTng、SystemTap,以及 kprobes、ftrace 等内核接口...这种方法称为 tracing。
但是如果修补 all 指令,即使用 SystemTap 探测 kernel.function("*"),您将扼杀性能,并可能导致系统崩溃。所以,你不能测量每一个函数调用,但是你可以测量每Nth函数调用,希望你能得到相同的结果,但是你需要大的sample(即运行程序几分钟)——这称为profiling。
Linux 附带分析器perf:
# perf record -- dd if=/dev/zero of=/dev/null
...
^C
# perf report
9.75% dd [kernel.kallsyms] [k] __clear_user
6.69% dd [kernel.kallsyms] [k] __audit_syscall_exit
5.61% dd [kernel.kallsyms] [k] fsnotify
4.73% dd [kernel.kallsyms] [k] system_call_after_swapgs
4.37% dd [kernel.kallsyms] [k] system_call
...
您也可以使用-g 来收集调用链。默认情况下perf 使用 CPU 性能计数器,因此在 N 个 CPU 周期后,会引发中断,并且 perf 处理程序(它已经嵌入到内核中)保存 IP。
如果您想收集堆栈,您可以使用 SystemTap 来完成:
# stap --all-modules -e '
probe timer.profile {
if(execname() == "dd") {
println("----");
print_backtrace(); }
}' -c 'dd if=/dev/zero of=/dev/null'
...
----
0xffffffff813e714d : _raw_spin_unlock_irq+0x32/0x3c [kernel]
0xffffffff81047bb9 : spin_unlock_irq+0x9/0xb [kernel]
0xffffffff8104ac68 : get_signal_to_deliver+0x4f0/0x528 [kernel]
0xffffffff8100216f : do_signal+0x48/0x4b1 [kernel]
0xffffffff81002608 : do_notify_resume+0x30/0x63 [kernel]
0xffffffff813edd6a : int_signal+0x12/0x17 [kernel]
在此示例中,SystemTap 使用timer.profile 探针,该探针附加到性能事件cpu-clock。为此,它生成、构建和加载内核模块。您可以通过stap -k -p 3 进行检查