我正在尝试监视进程在执行过程中调用了哪些函数。我的目标是了解一个流程在每个功能上花费了多少时间。函数被推送到堆栈上,并在函数调用返回时弹出。我想知道在内核代码中,这种推送和弹出实际上发生在哪里。
我在task_struct
中找到了一个void *stack
字段。我不确定这是否是我要找的领域。如果是,那么如何知道它是如何更新的呢?
我必须编写一个模块来利用这段代码。在这种情况下请帮帮我。
函数被推送到堆栈上,并在函数调用返回时弹出。我想知道在内核代码中,这种推送和弹出实际上发生在哪里。
这不会发生在内核代码中,而是由处理器完成的。也就是说,当x86汇编CPU找到call
指令时,它会将IP
推到堆栈上,而ret
指令会弹出该值。
您可以用call my_tracing_routine
修补内核中的每个call
和ret
指令,并在那里记录指令指针,然后将控制权传递给原始的被调用者/调用者。有一些工具可以做到这一点:LTTng、SystemTap以及内核内接口,如kprobes、ftrace。。。这种方法称为跟踪。
但是,如果修补所有指令,即使用SystemTap探针kernel.function("*")
,您将扼杀性能,并可能导致系统死机。因此,您不能测量每一次函数调用,但您可以测量每一次Nth功能调用,并希望您能得到等效的结果,但您需要大的样本(即运行程序几分钟),这称为评测。
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周期后,会引发中断,并且性能处理程序(它已经嵌入内核)会保存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使用连接到性能事件cpu-clock
的timer.profile
探测。为此,它生成、构建和加载内核模块。您可以使用stap -k -p 3
进行检查