书接上回:eBPF Talk: trace tracepoint 程序,本文将介绍在 bpf 里如何处理 tracepoint__data_loc 描述的字段。

关于 tracepoint __data_loc 的资料超级少,网络上能找到的资料如下:

不过,我深入研究了一下 kernel 里相关的源代码,发现了 __data_loc 的秘密。

tracepoint 中 string 类型的参数定义

P.S. 本文以 netlink:netlink_extack 为例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// ${KERNEL}/include/trace/events/netlink.h

TRACE_EVENT(netlink_extack,

    TP_PROTO(const char *msg),

    TP_ARGS(msg),

    TP_STRUCT__entry(
        __string(   msg,    msg )
    ),

    TP_fast_assign(
        __assign_str(msg, msg);
    ),

    TP_printk("msg=%s", __get_str(msg))
);

由上 tracepoint 的定义可知:

  1. tracepoint 的原型包括 const char *msg 参数;
  2. tracepoint 的参数是 msg
  3. tracepoint 的 entry struct 里有一个 __string 类型的 msg 字段;
  4. tracepoint 使用 “msg=%s” 进行输出。

__string 类型

__string 类型的定义如下:

1
2
3
4
5
6
7
// ${KERNEL}/include/trace/stages/stage1_struct_define.h

#undef __dynamic_array
#define __dynamic_array(type, item, len) u32 __data_loc_##item;

#undef __string
#define __string(item, src) __dynamic_array(char, item, -1)

其中使用了 __data_loc 前缀。

__get_str()

__get_str() 的定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ${KERNEL}/include/trace/stages/stage3_trace_output.h

#undef __get_dynamic_array
#define __get_dynamic_array(field)  \
        ((void *)__entry + (__entry->__data_loc_##field & 0xffff))

#undef __get_dynamic_array_len
#define __get_dynamic_array_len(field)  \
        ((__entry->__data_loc_##field >> 16) & 0xffff)

#undef __get_str
#define __get_str(field) ((char *)__get_dynamic_array(field))

tracepoint bpf 代码里获取 __data_loc 的方法

参考 __get_str() 的定义,tracepoint bpf 代码里获取 __data_loc 的方法如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct netlink_extack_error_ctx {
    unsigned long unused;

    __u32 msg; // __data_loc char[] msg;
};

SEC("tp/netlink/netlink_extack")
int tp__netlink_extack(struct netlink_extack_error_ctx *ctx)
{
    char *msg = (void *)(__u64) ((void *) ctx + (__u64) ((ctx->msg) & 0xFFFF));  // 获取其中的 msg

    __output_msg(ctx, msg, PROBE_TYPE_DEFAULT, 0);

    return 0;
}

小结

  • tracepoint 中 string 类型的参数定义:__string(msg, msg)
  • 实际上是 __data_loc_msg 字段;
  • bpftrace -lv 命令查看:__data_loc char[] msg
  • tracepoint bpf 代码里获取 __data_loc 的方法:char *msg = (void *)(__u64) ((void *) ctx + (__u64) ((ctx->msg) & 0xFFFF));

一切尽在源代码里,只是源代码并不好看明白。