linux gcc make tracing工具箱

Makefile中的执行过程

make --dry-run           只打印不执行make执行命令的过程
make V=1                   打印详细执行信息
make --debug

make --debug运行示意图

shell脚本的执行过程

#!/bin/sh -x              执行过程详细信息
sh -x script_name.sh   同上
set -x;function;set +x   通上,只打开区域间执行详细信息
sh -n scripte_name.sh	只做语法检查,而不执行脚本。

Gcc编译trace

gcc --save-temps hotplug.c -o hotplug
$ls hotplug*
hotplug  hotplug.c  hotplug.i  hotplug.i.orig  hotplug.o  hotplug.s
可以使用astyle格式化.i文件
astyle -A8 hotplug.i

Kbuild系统下编译trace选项

为某些对象增加编译选项
CFLAGS_foo.o = -O0 -g -save-temps
去除某些对象的编译选项
CFLAGS_REMOVE_foo.o = = -Os

内核ftrace

使用ftrace function

root@linux:/sys/kernel/debug/tracing# cat available_filter_functions |grep file_permission
security_file_permission
selinux_file_permission
apparmor_file_permission
root@linux:/sys/kernel/debug/tracing# echo security_file_permission > set_ftrace_filter 
root@linux:/sys/kernel/debug/tracing# echo function > current_tracer 
可选项,如果想要打印堆栈,可以echo 1 > options/func_stack_trace
root@linux:/sys/kernel/debug/tracing# sh -c 'echo 1 > tracing_on;echo $$ >set_ftrace_pid;exec cat enabled_functions;echo 0>tracing_on'
root@linux:/sys/kernel/debug/tracing# cat trace
# tracer: function
#
# entries-in-buffer/entries-written: 13/13   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
             cat-6430  [007] ....  2198.209159: security_file_permission <-vfs_write
             cat-6430  [007] ....  2198.213087: security_file_permission <-vfs_write
             cat-6430  [007] ....  2198.213174: security_file_permission <-vfs_read
             cat-6430  [007] ....  2198.213299: security_file_permission <-vfs_write
             cat-6430  [000] ....  2198.213656: security_file_permission <-vfs_read

在退出的时候记得将function ftrace关闭,不然其他的tracer不会生效

 echo nop > current_tracer 
 echo > set_ftrace_filter

限制:没有打印参数的功能,也不会打印返回值

funciton graph

和function的区别:function graph中设置追踪的函数之后会显示函数调用的所有函数及其时间间隔,而function显示调用到本函数的堆栈,是向上追溯的。
function graph主要用来看调用哪些函数,也可以用来查看时间消耗。

root@linux:/sys/kernel/debug/tracing# echo do_sys_open > set_graph_function 
root@linux:/sys/kernel/debug/tracing# echo 1 > tracing_on;echo $$ >set_ftrace_pid ;exec cat set_ftrace_pid;echo 0>tracing_on
root@linux:/t/tracing# cat trace
# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 7)               |  do_sys_open() {
 7)               |    getname() {
 7)               |      getname_flags() {
 7)               |        kmem_cache_alloc() {
 7)   0.132 us    |          _cond_resched();
 7)   0.108 us    |          _cond_resched();
 7)   0.122 us    |          memcg_kmem_put_cache();
 7)   3.438 us    |        }
 7)               |        __check_object_size() {
 7)   0.108 us    |          is_vmalloc_or_module_addr();
 7)   0.125 us    |          __virt_addr_valid();
 7)   0.102 us    |          __check_heap_object();
 7)   0.132 us    |          check_stack_object();
 7)   3.474 us    |        }
 7)   8.708 us    |      }
 7)   9.699 us    |    }

使用完毕后清理设置:

root@linux:/t/tracing# echo nop > current_tracer 
root@linux:/t/tracing# echo > set_graph_function 
root@linux:/t/tracing# echo > set_ftrace_pid 

原始的接口比较复杂,不太熟悉的话建议使用trace-cmd/kernelshark,其中kernelshark底层依赖于trace-cmd,但是它提供图形化操作,相对来说比较友好。

kprobe event

kprobe可以在任意的位置添加hook点,通常需要额外编写module来完成工作,而ftrace集成了kprobe功能,当我们只需要一些打印的时候可以使用这个kprobe_events接口,但是操作会比trace event要复杂一些,主要是创建kprobe event的过程比较复杂.
内核中的示例文档Documentation/trace/kprobetrace.txt介绍了基本的用法,就直接搬运过来了

  p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]	: Set a probe
  r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]		: Set a return probe
  -:[GRP/]EVENT						: Clear a probe

创建kprobe_event:

echo 'p:myprobe do_sys_open'> kprobe_events    销毁现有的并创建新的event,在调用的时候就触发,可以打印传入参数
echo 'r:myprobe do_fork' >> kprobe_events    追加return event,在返回的时候会触发,可以打印返回值

除了基本的使用之外,我们通常想要查看传入的参数,如何控制格式化打印输出呢

 FETCHARGS	: Arguments. Each probe can have up to 128 args.
  %REG		: Fetch register REG
  @ADDR		: Fetch memory at ADDR (ADDR should be in kernel)            //可以查看全局的静态变量,它的地址固定
  @SYM[+|-offs]	: Fetch memory at SYM +|- offs (SYM should be a data symbol) //可以查看全局的静态变量,它的地址固定
  $retval	: Fetch return value.(*)      //创建retprobe event的时候用到
  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
  NAME=FETCHARG : Set NAME as the argument name of FETCHARG. 打印更加直观
  FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types  格式化输出
		  (u8/u16/u32/u64/s8/s16/s32/s64), "string" and bitfield are supported.

以do_sys_open为例,类型为long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode),查看4个参数的具体值,能够更加直观地输出它的值:
1.函数传参通常都是通过寄存器,以x86_64为例,参考asmlinkage中的介绍,4个参数都会放到寄存器里:rdi rsi rdx rcx,但是这些寄存器名称是x86_64自己定义的,在内核中可能就不叫这个名称了,查找arch/x86/include/asm/ptrace.h,我们可以对应上:rdi->di,rsi->si,rdx->dx,rcx->cx,rax->ax

arch/x86/include/asm/calling.h
 x86 function call convention, 64-bit:
 -------------------------------------
  arguments           |  callee-saved      | extra caller-saved | return  | 
  [callee-clobbered]                                [callee-clobbered] 
 ---------------------------------------------------------------------------
 rdi rsi rdx rcx r8-9 | rbx rbp [*] r12-15 | r10-11             | rax, rdx [**]
 
arch/x86/include/asm/ptrace.h
struct pt_regs {
...
	unsigned long ax;
	unsigned long cx;
	unsigned long dx;
	unsigned long si;
	unsigned long di;
	unsigned long orig_ax;
...
};

但是第三个参数filename是一个指针,我们拿到的寄存器地址只是指向字符串的指针,对我们来说没有意义,我们需要将字符串读取出来:+|-offs(FETCHARG)->+0($si):string,最终我们可以通过echo 'p:myprobe do_sys_open dfd=%di filename=+0(%si):string flags=%dx ' > kprobe_events创建一个event,并且结构化输出:

teamviewerd-2352  [001] .... 55981.990255: myprobe: (do_sys_open+0x0/0x290) dfd=0xffffff9c filename="/sys/devices/virtual/tty/tty0/active" flags=0x8000

有人写了一个更加复杂的例子:打印报文假,设函数第一个参数是skb,并且skb->data指向ip头(协议栈中大部分时候都是这种情况),我们就可以打印出一些有用的信息,如下

skb=%di head=+0x18(%di) data=+0x20(%di) mac=+0x38(%di):u32 dev=+0(+16(%di)):string sip=+12(+0x20(%di)):u32 sport=+20(+0x20(%di)):u16 dip=+16(+0x20(%di)):u32 dport=+22(+0x20(%di)):u16

其中“head=+0x18(%di)”的0x18这个偏移需要根据当前内核中sk_buff结构体来确定,其他的几个偏移也一样。

开启kprobe_event:
之后就能在events/kprobes/myprobe/下看到创建的event,之后的所有操作就和静态的trace event一样了,可以通过echo 1 > enable使能一组event,也可以控制单独的event.(记得设置current_trace为nop,这时候event才生效).
关闭kprobe_event:

echo 0 > enable    可以控制一个组,单独的event,所有的kprobe event

销毁kprobe_event:

echo '-:do_sys_open' > kprobe_events    销毁单独的event
echo '-:myprobe' > kprobe_events    销毁myprobe组下所有的event

参考

https://events.linuxfoundation.org/wp-content/uploads/2017/12/oss-eu-2018-fun-with-dynamic-trace-events_steven-rostedt.pdf
https://blog.csdn.net/cll__/article/details/51417291

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值