系统中存在很多linux内核默认预设的trace events,其目的是方便大家查相同问题时能够复用他们的tracepoint,基于此,本文章根据linux内核现成的trace event做分享,主要聊聊如何使用人家定义好的event,从而方便大家调试问题
trace events的目录如下:
# ls /sys/kernel/debug/tracing/events/ alarmtimer fib6 namei sched android_fs filelock napi scmi asoc filemap neigh signal avc ftrace net skb binder gadget nfs smbus block gpio nfs4 sock bpf_test_run header_event nfsd spi bpf_trace header_page nvme sunrpc bridge hwmon oom swiotlb btrfs i2c pagefault sync_trace cfg80211 initcall page_isolation task cgroup iomap pagemap tcp cifs iommu page_pool thermal clk io_uring percpu thermal_ipa_power cma ipi power thermal_power_allocator compaction irq printk timer cpufreq_interactive jbd2 pwm udp cpuhp kmem qdisc v4l2 devfreq kvm ras vb2 dma_fence kyber raw_syscalls virtio_gpu drm mac80211 rcu vmscan dwc3 mali regmap workqueue emulation mdio regulator writeback enable migrate rpcgss xdp error_report mmap rpm xfs ext4 mmc rseq xhci-hcd fib module rtc
我们可以看到linux提供了107个trace system,我们在查问题的时候可以先考虑这些trace system
这里还是以经典函数block_rq_complete为例,可以看到其内容如下:
# find /sys/kernel/debug/tracing/events/block/block_rq_complete/ /sys/kernel/debug/tracing/events/block/block_rq_complete/ /sys/kernel/debug/tracing/events/block/block_rq_complete/format /sys/kernel/debug/tracing/events/block/block_rq_complete/trigger /sys/kernel/debug/tracing/events/block/block_rq_complete/filter /sys/kernel/debug/tracing/events/block/block_rq_complete/id /sys/kernel/debug/tracing/events/block/block_rq_complete/enable
关于trace evemt的文章,可以查看内核官方文档如下,这里就不按照文档重复讨论了:
https://www.kernel.org/doc/html/latest/trace/events.html
本章主要聚焦在下面这几个文件的使用上:
enable filter format id trigger
顾名思义,这个就是打开此event的事件,方法如下:
echo 1 > enable
这里和tracepoint基础编程介绍有点重复,就无需展开了。
这里能够查看这个event的格式,我们在tracepoint基础编程介绍可以知道定义一个TRACE_EVENT需要包含五要素:name/prototype/args/struct/assign/print
,这里是具体展现。如下示例:
# cat format name: block_rq_complete ID: 1350 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:dev_t dev; offset:8; size:4; signed:0; field:sector_t sector; offset:16; size:8; signed:0; field:unsigned int nr_sector; offset:24; size:4; signed:0; field:int error; offset:28; size:4; signed:1; field:char rwbs[8]; offset:32; size:8; signed:0; field:__data_loc char[] cmd; offset:40; size:4; signed:0; print fmt: "%d,%d %s (%s) %llu + %u [%d]", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) ((REC->dev) & ((1U << 20) - 1))), REC->rwbs, __get_str(cmd), (unsigned long long)REC->sector, REC->nr_sector, REC->error
这里我们知道如下信息:
这个trace里面,结构体提供了从common_type到cmd的所有字段域 提供了这个struct的offset,也就是具体位置 提供了打印格式
这里提供了id,这个id代表这个trace的id,我们可以从format上看到,如下:
ID: 1350
这个id是只读的,所以我们只能读到这个id,用作filter过滤
# cat id 1350
这个用作过滤,我们可以知道,如果直接enable,我们没办法进行过滤,所以可以在这里添加多个过滤条件,如下:
假设我们只想看idle进程的信息,则如下:
echo "common_pid==0" > filter
如果想要关闭filter,直接echo 0即可
echo 0 > filter
注意,这里的filter只能是struct的成员,如果不是,就会失效,所以提前需要cat format看一下struct哪些成员可以过滤
trigger是trace默认提供的触发类型,我们可以通过cat获取可以使用的trigger类型,如下:
# cat trigger # Available triggers: # traceon traceoff stacktrace enable_event disable_event
这里提供了6个trigger,我们如果想要echo trigger,这里需要指定的语法格式,如下:
echo 'command[:count] [if filter]' > trigger
这里可以看到,我们需要填入command: trigger类型,count
次数 if filter 过滤条件。这里是检测过滤条件满足的时候,一定次数下直接打开此trace,如下示例
echo 'traceon:5 if common_pid==0' > trigger
这里意思是如果pid为idle下触发blk_update_request达到5次,则主动打开此trace event
echo 'traceoff:5 if common_pid==0' > trigger
这里意思是如果pid为idle下触发blk_update_request达到5次,则主动关闭此trace event
这里是打印此函数的堆栈,可以示例如下:
echo "stacktrace:1 if common_pid==0" > trigger
这里是当pid是idle的时候,打印一次堆栈,这样日志如下:
<idle>-0 [000] .Ns. 3780.543922: <stack trace> => trace_event_buffer_commit => trace_event_raw_event_block_rq_complete => blk_update_request => mmc_blk_cqe_complete_rq => mmc_blk_mq_complete => blk_done_softirq => __do_softirq => irq_exit => __handle_domain_irq => gic_handle_irq => el1_irq => cpuidle_enter_state => cpuidle_enter => call_cpuidle => do_idle => cpu_startup_entry => rest_init => arch_call_rest_init => start_kernel
这是打开对于的event的trigger,如下:
echo "enable_event:block:block_rq_complete:2" > trigger
当block_rq_complete被触发两次时,打开event
对于我们有关闭event的trigger,如下:
echo 'disable_event:block:block_rq_complete:2' > trigger
当block_rq_complete被触发两次时,关闭event
至此,我们简单了解了trace event的使用。