Linux内核之tracepoint介绍
概述
Tracepoint
是一种在 Linux 内核中插入的静态探测点,用于跟踪和调试内核行为。它允许开发者在特定位置插入代码,收集运行时信息,而不会显著影响性能。
原理
特性
- 静态探测点: 在内核预定义, 自定义的函数可以插入在这里
- 动态启用: 默认不启用, 减少性能开销
- 低开销
定义
有以下几种宏定义的方式:
TRACE_EVENT
DECLARE_TRACE
DEFINE_TRACE
以sched_switch为例, 这个是用TRACE_EVENT
宏定义的, 他会生成一个trace_xxx的函数
/* |
使用
static void __sched notrace __schedule(unsigned int sched_mode) { |
分类
完整的探针可以用如下命令查看
sudo bpftrace -l "tracepoint:*" |
如果要查看带分类的,树形结构,可以通过这种方式查看
sudo tree /sys/kernel/debug/tracing/events -I 'enable|filter|format|id|trigger' |
下面将列举部分子系统的tracepoint
1. 块设备层(Block Layer)
用于跟踪块设备 I/O 操作。
Tracepoint 事件名称 | 描述 |
---|---|
block:block_bio_backmerge | 当两个 BIO 请求向后合并时触发 |
block:block_bio_bounce | 当 BIO 请求需要 bounce buffer 时触发 |
block:block_bio_complete | 当 BIO 请求完成时触发 |
block:block_bio_frontmerge | 当两个 BIO 请求向前合并时触发 |
block:block_bio_queue | 当 BIO 请求被加入队列时触发 |
block:block_bio_remap | 当 BIO 请求被重映射时触发 |
block:block_dirty_buffer | 当 buffer 被标记为脏时触发 |
block:block_getrq | 当获取一个请求时触发 |
block:block_plug | 当 block 层被 plug(暂停)时触发 |
block:block_rq_complete | 当请求完成时触发 |
block:block_rq_insert | 当请求被插入队列时触发 |
block:block_rq_issue | 当请求被下发到设备时触发 |
block:block_rq_merge | 当请求被合并时触发 |
block:block_rq_remap | 当请求被重映射时触发 |
block:block_rq_requeue | 当请求被重新加入队列时触发 |
block:block_sleeprq | 当请求因资源不足而进入睡眠时触发 |
block:block_split | 当请求被分割时触发 |
block:block_touch_buffer | 当 buffer 被访问时触发 |
block:block_unplug | 当 block 层被 unplug(恢复)时触发 |
2. 调度器(Scheduler)
- 用于跟踪进程调度相关事件。
Tracepoint 事件名称 | 描述 |
---|---|
sched:sched_kthread_stop | 当内核线程被停止时触发 |
sched:sched_kthread_stop_ret | 当内核线程停止操作返回时触发 |
sched:sched_migrate_task | 当任务从一个 CPU 迁移到另一个 CPU 时触发 |
sched:sched_move_numa | 当任务在 NUMA 节点之间迁移时触发 |
sched:sched_pi_setprio | 当任务的优先级(PI,优先级继承)被设置时触发 |
sched:sched_process_exec | 当进程执行一个新的程序时触发 |
sched:sched_process_exit | 当进程退出时触发 |
sched:sched_process_fork | 当进程 fork 出一个新进程时触发 |
sched:sched_process_free | 当进程资源被释放时触发 |
sched:sched_process_hang | 当进程挂起时触发 |
sched:sched_process_wait | 当进程进入等待状态时触发 |
sched:sched_stat_blocked | 统计任务被阻塞的时间 |
sched:sched_stat_iowait | 统计任务等待 I/O 的时间 |
sched:sched_stat_runtime | 统计任务运行的时间 |
sched:sched_stat_sleep | 统计任务睡眠的时间 |
sched:sched_stat_wait | 统计任务等待的时间 |
sched:sched_stick_numa | 当任务被固定在某个 NUMA 节点时触发 |
sched:sched_swap_numa | 当任务在 NUMA 节点之间交换时触发 |
sched:sched_switch | 当任务切换发生时触发 |
sched:sched_wait_task | 当任务进入等待状态时触发 |
sched:sched_wake_idle_without_ipi | 当唤醒空闲 CPU 且不需要发送 IPI(处理器间中断)时触发 |
sched:sched_wakeup | 当任务被唤醒时触发 |
sched:sched_wakeup_new | 当新创建的任务被唤醒时触发 |
sched:sched_waking | 当任务即将被唤醒时触发 |
3. 文件系统(File System)
- 用于跟踪文件系统操作。
- 示例:
ext4:ext4_read_file
:ext4 文件系统的读操作。xfs:xfs_file_read
:XFS 文件系统的读操作。
4. 网络(Network)
- 用于跟踪网络数据包的收发和处理。
- 示例:
net:netif_rx
:网络设备接收数据包。net:net_dev_queue
:网络设备发送数据包。
Tracepoint 事件名称 | 描述 |
---|---|
net:napi_gro_frags_entry | 当 NAPI 的 GRO(Generic Receive Offload)处理分片数据包开始时触发 |
net:napi_gro_frags_exit | 当 NAPI 的 GRO 处理分片数据包结束时触发 |
net:napi_gro_receive_entry | 当 NAPI 的 GRO 处理接收数据包开始时触发 |
net:napi_gro_receive_exit | 当 NAPI 的 GRO 处理接收数据包结束时触发 |
net:net_dev_queue | 当网络设备将数据包加入队列时触发 |
net:net_dev_start_xmit | 当网络设备开始发送数据包时触发 |
net:net_dev_xmit | 当网络设备发送数据包时触发 |
net:net_dev_xmit_timeout | 当网络设备发送数据包超时时触发 |
net:netif_receive_skb | 当内核接收一个 skb(socket buffer)时触发 |
net:netif_receive_skb_entry | 当内核开始接收一个 skb 时触发 |
net:netif_receive_skb_exit | 当内核完成接收一个 skb 时触发 |
net:netif_receive_skb_list_entry | 当内核开始接收一个 skb 列表时触发 |
net:netif_receive_skb_list_exit | 当内核完成接收一个 skb 列表时触发 |
net:netif_rx | 当网络接口接收数据包时触发 |
net:netif_rx_entry | 当网络接口开始接收数据包时触发 |
net:netif_rx_exit | 当网络接口完成接收数据包时触发 |
net:netif_rx_ni_entry | 当网络接口在非中断上下文中开始接收数据包时触发 |
net:netif_rx_ni_exit | 当网络接口在非中断上下文中完成接收数据包时触发 |
5. 系统调用(System Calls)
- 用于跟踪系统调用的进入和退出。
- 示例:
syscalls:sys_enter_open
:open
系统调用的进入。syscalls:sys_exit_open
:open
系统调用的退出。
6. 内存管理(Memory Management)
- 用于跟踪内存分配和释放。
- 示例:
kmem:kmalloc
:内核内存分配。kmem:kfree
:内核内存释放。
Tracepoint 事件名称 | 描述 |
---|---|
kmem:kfree | 当内存被释放(kfree )时触发 |
kmem:kmalloc | 当内存通过 kmalloc 分配时触发 |
kmem:kmalloc_node | 当内存通过 kmalloc_node 在指定 NUMA 节点分配时触发 |
kmem:kmem_cache_alloc | 当内存通过 slab 分配器(kmem_cache_alloc )分配时触发 |
kmem:kmem_cache_alloc_node | 当内存通过 slab 分配器在指定 NUMA 节点(kmem_cache_alloc_node )分配时触发 |
kmem:kmem_cache_free | 当内存通过 slab 分配器释放(kmem_cache_free )时触发 |
kmem:mm_page_alloc | 当物理页被分配时触发 |
kmem:mm_page_alloc_extfrag | 当物理页分配时发生外部碎片化时触发 |
kmem:mm_page_alloc_zone_locked | 当物理页在锁定内存区域(zone)中分配时触发 |
kmem:mm_page_free | 当物理页被释放时触发 |
kmem:mm_page_free_batched | 当物理页批量释放时触发 |
kmem:mm_page_pcpu_drain | 当每 CPU 页缓存(per-CPU page cache)被清空时触发 |
kmem:rss_stat | 当进程的 RSS(Resident Set Size)统计信息更新时触发 |
7. 中断(Interrupts)
- 用于跟踪硬件中断。
- 示例:
irq:irq_handler_entry
:中断处理函数进入。irq:irq_handler_exit
:中断处理函数退出。
Tracepoint 事件名称 | 描述 |
---|---|
irq:irq_handler_entry | 当中断处理程序开始执行时触发 |
irq:irq_handler_exit | 当中断处理程序执行结束时触发 |
irq:softirq_entry | 当软中断处理程序开始执行时触发 |
irq:softirq_exit | 当软中断处理程序执行结束时触发 |
irq:softirq_raise | 当软中断被触发(raise)时触发 |
8. TLB
- 用于跟踪TLB事件
事件名称 | 描述 |
---|---|
tlb:tlb_flush |
当 TLB 被刷新时触发。TLB 刷新通常发生在页表更新、上下文切换或进程地址空间变化时。 |
tlb_flush_mmu |
当 MMU(Memory Management Unit)执行 TLB 刷新时触发。 |
tlb_flush_range |
当 TLB 刷新特定地址范围时触发。 |
tlb_flush_all |
当整个 TLB 被刷新时触发。 |
tlb_flush_kernel_range |
当内核地址空间的 TLB 刷新时触发。 |
tlb_flush_user_range |
当用户地址空间的 TLB 刷新时触发。 |
9. 系统调用
用于统计各种系统调用
事件名称 | 描述 |
---|---|
sys_enter_xx | 进入xx系统调用 |
sys_exit_xx | 退出xx系统调用 |
使用
通过bpftrace
这个脚本将会追踪TLB刷新的事件,并打印他的进程名和pid
sudo bpftrace -e 'tracepoint:tlb:tlb_flush { printf("TLB flushed by %s (pid: %d)\n", comm, pid); }’ |
另一个脚本, 将追踪irq处理的统计
sudo bpftrace -e 'tracepoint:irq:irq_handler_entry { @[args->irq, str(args->data_loc_name)] = count(); } END { printf("\nInterrupt count by IRQ number and name:\n"); print(@); }' |
某个tracepoint的参数可以通过ftrace提供的接口查看
sudo cat /sys/kernel/debug/tracing/events/irq/irq_handler_entry/format |
显示如下:
name: irq_handler_entry |
*注意name的引用, 要加data_loc_指定类型才可以
其他内核探测方案
kprobe
Kprobe
是动态插入的探测点,可以在运行时插入到任意内核函数中。灵活性更高,但是性能相对也会更差一些,具体的对比如下
特性 | Tracepoint | Kprobe |
---|---|---|
性能开销 | 低 | 较高 |
稳定性 | 高(接口稳定) | 低(依赖内核函数实现) |
灵活性 | 低(只能追踪预定义的事件) | 高(可以追踪任意内核函数) |
实现方式 | 静态(内核代码中预定义) | 动态(运行时插桩) |
适用场景 | 生产环境监控 | 调试和开发环境 |
总结
- 通过使用tracepoint,我们可以掌握linux内核运行的内部信息和应用程序是如何与内核进行交互的
- 通过bpftrace,可以更加方便的使用tracepoint,并且可以实现更多的功能
- 可以通过ftrace提供的文件系统接口,查看tracepoint的定义和参数
Linux内核之tracepoint介绍