linux动态调试工具,Linux内核常用的动态调试手段

本文介绍 linux 内核中几种常用的动态调试手段,也都是我常用的,都是在生产环境中直接使用, 不需要借助工具, 依照我的经验,去客户生产环境中解决问题,很多都不会预装perf、BPF工具,有的即使有perf这样的工具,也因为 工具 性能影响客户几千亿美金的正常业务,也会不让你使用。

最常见的就是使用kprobe,kprobe具体的原理可以参考我以前写的这篇文章kprobe原理

使用kprobe最常用的就是查询函数调用的参数和返回值:

cd /sys/kernel/debug/tracing

echo ‘p:myprobe do_sys_open fname_str=+0(%si):string' > kprobe_events

echo 'r:myretprobe do_sys_open $retval' >> kprobe_events

ls  ./events/kprobes/

enable  filter myprobe myretprobe

使能kprobe event:

echo 1 >  ./events/kprobes/myprobe/enable

echo 1 >  ./events/kprobes/myretprobe/enable

cat  ./trace (可以看到系统中调用open的文件名和返回值)

eefa8835123ec6a2d432a697c5d74755.png

至于在kprobe_events文件中指定的寄存器(%si),基于具体平台的不同会不一样,可以预先使用perf工具查询到当前平台上获取函数参数的寄存器。

[root@u-jeff ~]# perf probe  'do_sys_open filename:string flags:u32'

Added new event:

...

[root@u-jeff ~]# cat /sys/kernel/debug/tracing/kprobe_events

p:probe/do_sys_open _text+2391184 filename_string=+0( %si ):string flags_u32= %dx :u32

第二类就是使用trace event:

比如像linux内核中最著名的函数__schedule()

static void __sched notrace __schedule(bool preempt)

{

...

trace_sched_switch(preempt, prev, next);

...

}

这种原理不用多说,是内核代码编译的时候就已经内嵌于函数中的,只需打开对应开关就可以看到固定格式的打印信息。

#pwd

/sys/kernel/debug/tracing/events/sched/sched_switch

#cat /sys/kernel/debug/tracing/trace

80760bc81b63a926c1bf30994dcd4602.png

系统中大量子系统中的函数都嵌入了这种trace event,只需针对性打开开关便可。

#pwd

/sys/kernel/debug/tracing/events

0a56bf96b60474f50888ebc3a31b21c3.png

还有一些函数里面没有内嵌trace event,或者有时想查看函数的调用堆栈,可以使用trace function + stack_trace

cd /sys/kernel/debug/tracing/

echo 0 > ./tracing_on

echo function > current_tracer

echo 1 > options/func_stack_trace

echo "your function" > set_ftrace_filter

echo 1 > tracing_on

比如把 ”your function" 改成 schedule

cat ./trace   可以看到调用schedule时的函数堆栈,而不需要在函数中加上WARN_ON(1)这样的语句再重新编译内核。

a323b79ba0d19ef9f121e32cd1ee4dfb.png

不管是function tracer还是 function_graph 这样的tracer,

cat ./available_tracers

hwlat blk function_graph wakeup_dl wakeup_rt wakeup function nop

我最佩服的就是实现此功能时同步代码段的方法,其实就是怎样从下面第一幅图变成第二幅图:

图一:

55397b186845f8ec1a086a73a878d4d2.png

图二:

134514943329ce1e60e9693d4ef80f67.png

因为实现这些功能的原理就是在代码段函数头中插上预先设定好的函数调用,如果刚刚插入代码时,正好有程序跑到这块代码就好玩了,系统很可能会崩掉,在x86系统上采用的就是先在函数头中插入一个字节指令'cc',当其它进程执行此指令时,系统进入int3中断流程,

所以在do_int3中可以看到下面类似的代码:

1cba68e19151c761696ef22fc7568171.png

当插入‘cc'之后,把'cc'指令同步到所有的cpu,然后把’cc‘之后的代码换成函数调用代码,最后把’cc'换掉,一切都ok.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值