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を検討してみると良いと思います.