ebpf 札记(3): 一个跟踪 kernel I/O request 生命周期的脚本
上周睡眠一直有问题,没有继续看 samples 里面的代码。写点别的来充数。
现在在追踪 kernel I/O request ,一个 request 被创建出来之后,会在多个函数中传递,直到被释放。
这个过程中它被那些内核函数调用?这是一个复杂的问题。幸好很多处理 request 的函数都是以 struct request*
作为入参,
这给我们一个跟踪 request 的思路。
同样的思路我还用来跟踪 nginx 里面的运行过程。
#!/usr/bin/env bpftrace
BEGIN {
@count = 0;
}
kretprobe:blk_mq_alloc_request
{
if (@req == 0) {
@req = retval;
printf("%llu, %d, %s\n", (struct request *)@req, pid, probe);
printf("comm: %s, kstack: \n%s\n", comm, kstack);
}
if (retval == @req) {
printf("duplicated req: %d, %llu\n", pid, @req);
}
}
kprobe:blk_*
{
if (@req != 0) {
if (arg1 == @req || arg0 == @req) {
$rid = arg0;
if (arg1 == @req) {
$rid = arg1
}
printf("%llu, %s\n", (struct request *)$rid, probe);
printf("%llu, %s\n%s\n", @req, probe, kstack);
}
}
}
kprobe:blk_mq_free_request
/ @req > 0 /
{
if (@req == arg0) {
@count += 1;
if (@count > 3) {
exit();
}
@req = 0;
}
}
现在 bpftrace 的 kprobe section 不支持更复杂的匹配方式,倒是挺遗憾的。我想 「匹配所有 blk_ 开头的 kprobe 但是 blk_mq_free_request 除外」,但是没有找到方法实现。
我还糊了一个 python 脚本解释拿到的栈:
(root): entry_SYSCALL_64_after_hwframe
|
\-entry_SYSCALL_64_after_hwframe
|
\-do_syscall_64
|
\-__x64_sys_ioctl
|
\-sg_ioctl
|
\-sg_ioctl_common
|
\-sg_new_write.isra.0
|
\-sg_common_write.isra.0
|
\-kretprobe_trampoline(blk_mq_alloc_request)
|
\-blk_rq_map_user_iov
|
\-blk_account_io_start
|
\-blk_mq_sched_insert_request
|
\-blk_mq_run_hw_queue
|
\-__blk_mq_delay_run_hw_queue
|
\-__blk_mq_run_hw_queue
|
\-blk_mq_sched_dispatch_requests
|
\-__blk_mq_sched_dispatch_requests
|
\-blk_mq_get_driver_tag
|
\-blk_mq_dispatch_rq_list
|
\-blk_mq_start_request
|
\-scsi_queue_rq
|
\-blk_add_timer
|
\-blk_rq_map_user
|
\-blk_rq_append_bio
|
\-blk_execute_rq_nowait
|
\-blk_mq_request_bypass_insert
(root): secondary_startup_64_no_verify
|
\-secondary_startup_64_no_verify
|
\-start_secondary
|
\-cpu_startup_entry
|
\-do_idle
|
\-flush_smp_call_function_from_idle
|
\-do_softirq
|
\-__softirqentry_text_start
|
\-blk_done_softirq
|
\-blk_complete_reqs
|
\-scsi_complete
|
\-scsi_finish_command
|
\-scsi_io_completion
|
\-scsi_end_request
|
\-blk_stat_add
|
\-blk_account_io_done
|
\-__blk_mq_end_request
|
\-blk_put_request
|
\-sg_rq_end_io
|
\-blk_mq_free_request
TODO:
- 为什么
blk_mq_alloc_request
会重入?