BPF_PROG_TYPE_RAW_TRACEPOINT (raw tracepoint) について
BPFのプログラムタイプの一つにBPF_PROG_TYPE_RAW_TRACEPOINTがあります (commit).これを利用するとtracepointの変換前の引数にアクセスすることができます.
例としてkernel/sched/core.cで定義されるsched_swtich
のtracepointを考えます.
trace_sched_switch(preempt, prev, next);
このtracepointはinclude/trace/events.sched.hで定義されます.
TRACE_EVENT(sched_switch, TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next), TP_ARGS(preempt, prev, next), TP_STRUCT__entry( __array( char, prev_comm, TASK_COMM_LEN ) __field( pid_t, prev_pid ) __field( int, prev_prio ) __field( long, prev_state ) __array( char, next_comm, TASK_COMM_LEN ) __field( pid_t, next_pid ) __field( int, next_prio ) ), TP_fast_assign( memcpy(__entry->next_comm, next->comm, TASK_COMM_LEN); __entry->prev_pid = prev->pid; __entry->prev_prio = prev->prio; __entry->prev_state = __trace_sched_switch_state(preempt, prev); memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN); __entry->next_pid = next->pid; __entry->next_prio = next->prio; /* XXX SCHED_DEADLINE */ ), TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", __entry->prev_comm, __entry->prev_pid, __entry->prev_prio, (__entry->prev_state & (TASK_REPORT_MAX - 1)) ? __print_flags(__entry->prev_state & (TASK_REPORT_MAX - 1), "|", { TASK_INTERRUPTIBLE, "S" }, { TASK_UNINTERRUPTIBLE, "D" }, { __TASK_STOPPED, "T" }, { __TASK_TRACED, "t" }, { EXIT_DEAD, "X" }, { EXIT_ZOMBIE, "Z" }, { TASK_PARKED, "P" }, { TASK_DEAD, "I" }) : "R", __entry->prev_state & TASK_REPORT_MAX ? "+" : "", __entry->next_comm, __entry->next_pid, __entry->next_prio) );
ここで,通常のtracepointであれば,TP_STRUCT__entry
で定義されている項目 (+ tracepointに共通の項目)にアクセスできます.これは以下のtracing/events/sched/sched_switch/format
からも確認できます.
# cat /sys/kernel/debug/tracing/events/sched/sched_switch/format name: sched_switch ID: 318 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:char prev_comm[16]; offset:8; size:16; signed:1; field:pid_t prev_pid; offset:24; size:4; signed:1; field:int prev_prio; offset:28; size:4; signed:1; field:long prev_state; offset:32; size:8; signed:1; field:char next_comm[16]; offset:40; size:16; signed:1; field:pid_t next_pid; offset:56; size:4; signed:1; field:int next_prio; offset:60; size:4; signed:1; print fmt: "prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", REC->prev_comm, REC->prev_pid, REC->prev_prio, (REC->prev_state & ((((0x0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040) + 1) << 1) - 1)) ? __print_flags(REC->prev_state & ((((0x0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040) + 1) << 1) - 1), "|", { 0x0001, "S" }, { 0x0002, "D" }, { 0x0004, "T" }, { 0x0008, "t" }, { 0x0010, "X" }, { 0x0020, "Z" }, { 0x0040, "P" }, { 0x0080, "I" }) : "R", REC->prev_state & (((0x0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040) + 1) << 1) ? "+" : "", REC->next_comm, REC->next_pid, REC->next_prio
一方でraw tracepiontの場合,アクセスできるのはtrace_sched_switch()
に与えた引数 (TP_PROTO
の引数)になります.sched_switch
の場合は
TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)
になります.
raw tracepintのもともとの導入の経緯は,sched_switch
においてprev
とnext
のtask_struct
の中身にアクセスしたいという要求からだったようです(参考).通常のtracepointであればBPFプログラム呼び出し前にtracepointのデータを整形する必要がありますが,raw tracepointではその必要がないため,その分のオーバヘッドが抑えられます.ただし,raw tracepointの引数で与えられたポインタのデータにアクセスするにはbpf_probe_read()
を使う必要があります.基本的にはtarcepiontの方が扱いやすいので,そちらを使えば良いと思いますが,パフォーマンスが問題であったり,あるいはtracepointの情報だけでは不足している場合はraw tracepointを検討してみると良いと思います.