bpftrace内置变量
-
pid进程 ID(内核 tgid) -
tid线程 ID(内核 pid) -
uid用户身份 -
gid组 ID -
nsecs纳秒时间戳 -
elapsed自 bpftrace 初始化以来的纳秒 -
cpu处理器 ID -
comm进程名称 -
kstack内核堆栈跟踪 -
ustack用户堆栈跟踪 -
arg0 arg1 argN跟踪函数的参数;假定为 64 位宽 -
sarg0 sarg1 sargN跟踪函数的参数(对于在堆栈上存储参数的程序);假定为 64 位宽 -
retval跟踪函数的返回值 -
func跟踪函数的名称 -
probe探头的全名 -
curtask当前任务结构为 u64 -
rand随机数作为 u32 -
cgroup当前进程的cgroup ID -
cpidc command子pid(u32),仅对标志有效 -
$1 $2 $N $#bpftrace 程序的位置参数
特殊变量:
-
@global_name全局变量 -
@thread_local_variable_name[tid]每线程变量 -
$scratch_name临时变量
bpftrace 内置接口
count(): 频率计数
hist(), lhist(): 直方图
nsecs: 时间戳和时间增量
kstack:堆栈跟踪,内核
ustack:堆栈跟踪,用户
$1, ..., $N, $#: 位置参数 如果它是非数字的,则它必须在str()调用中用作字符串。$#返回提供的位置参数的数量。
-
printf(char *fmt, ...)打印格式化 -
time(char *fmt)打印格式化时间 -
join(char *arr[] [, char *delim])打印数组 -
str(char *s [, int length])返回 s 指向的字符串 -
ksym(void *p)解析内核地址 -
usym(void *p)解析用户空间地址 -
kaddr(char *name)解析内核符号名称 -
uaddr(char *name)解析用户级符号名称 -
reg(char *name)返回存储在命名寄存器中的值 -
system(char *fmt)执行shell命令 -
exit()退出 bpftrace -
cgroupid(char *path)解析 cgroup ID -
kstack([StackMode mode, ][int level])内核堆栈跟踪 -
ustack([StackMode mode, ][int level])用户堆栈跟踪 -
ntop([int af, ]int|char[4|16] addr)将 IP 地址数据转换为文本 -
cat(char *filename)打印文件内容 -
signal(char[] signal | u32 signal)向当前任务发送信号 -
strncmp(char *s1, char *s2, int length)比较两个字符串的前 n 个字符 -
override(u64 rc)覆盖返回值 -
buf(void *d [, int length])返回 d 指向的数据的十六进制格式字符串 -
sizeof(...)返回类型或表达式的大小 -
print(...)使用默认格式打印非地图值 -
strftime(char *format, int nsecs)返回格式化的时间戳 -
path(struct path *path)返回完整路径 -
uptr(void *p)注释为用户空间指针 -
kptr(void *p)注释为内核空间指针 -
macaddr(char[6] addr)转换 MAC 地址数据
其中一些是异步的:内核将事件排队,但一段时间后(毫秒)它在用户空间中处理。异步操作是:printf()、time()和join()。ksym() 和usym()以及变量kstack和都ustack同步记录地址,然后异步进行符号转换。
bpftrace Map Functions
-
count()计算这个函数被调用的次数 -
sum(int n)总结 -
avg(int n)平均值 -
min(int n)记录看到的最小值 -
max(int n)记录看到的最大值 -
stats(int n)返回此值的计数、平均值和总计 -
hist(int n)生成 n 值的 log2 直方图 -
lhist(int n, int min, int max, int step)生成 n 值的线性直方图 -
delete(@x[key])删除作为参数传入的地图元素 -
print(@x[, top [, div]])打印地图,可选择仅顶部条目和除数 -
print(value)打印一个值 -
clear(@x)从地图中删除所有键 -
zero(@x)将所有地图值设置为零
其中一些是异步的:内核将事件排队,但一段时间后(毫秒)它在用户空间中处理。异步操作是:print()在地图上clear()、 和zero()。
bpftrace Output
printf(): 每事件输出
interval.:间隔输出
hist(), print(): 直方图打印
bpftrace探针
kprobe检测函数执行的开始,kretprobe检测结束(返回)。
kprobe: arg0, arg1, ..., argN
kretprobe: retval 可以通过这些变量名称访问参数。arg0是第一个参数,只能使用kprobe
retval是检测函数的返回值,只能在kretprobe使用
uprobe检测用户级函数执行的开始,并uretprobe检测结束(它的返回)。
要列出可用的 uprobes,您可以使用任何程序从二进制文件中列出文本段符号,例如objdump和nm。例如:
# objdump -tT /bin/bash | grep readline
例子:
# bpftrace -e 'uretprobe:/bin/bash:readline { printf("read a line\n"); }'
tracepoint
可以从 /sys 中的 /format 文件中列出每个跟踪点的可用成员。例如:
# cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format
profile: 定时采样事件
profile:hz:rate
profile:s:rate
profile:ms:rate
profile:ms:rate
interval: 定时输出
interval:ms:rate
interval:s:rate
interval:us:rate
interval:hz:rate
software: 预定义的软件事件
software:event_name:count
software:event_name:
这些是 Linux 内核提供的预定义软件事件,通常通过 perf 实用程序进行跟踪。它们类似于跟踪点,但只有十几个,它们记录在 perf_event_open(2) 手册页中。事件名称是:
-
cpu-clock或cpu -
task-clock -
page-faults或faults -
context-switches或cs -
cpu-migrations -
minor-faults -
major-faults -
alignment-faults -
emulation-faults -
dummy -
bpf-output
hardware: 预定义的硬件事件
hardware:event_name:count
hardware:event_name:
这些是 Linux 内核提供的预定义硬件事件,通常由 perf 实用程序跟踪。它们是使用性能监控计数器 (PMC) 实现的:处理器上的硬件资源。其中大约有十个,它们记录在 perf_event_open(2) 手册页中。
BCC探针
kprobe
kprobe__ kernel_function_name
-
struct pt_regs *ctx寄存器和 BPF 上下文。 -
struct sock *sktcp_v4_connect() 的第一个参数。
第一个参数总是struct pt_regs *,其余的是函数的参数(如果您不打算使用它们,则不需要指定它们)。pt_regs 结构具体架构具体分析,获取其中的参数有一些宏
#define PT_REGS_ARM64 const volatile struct user_pt_regs
#define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
#define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
#define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
#define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])
#define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])
#define PT_REGS_RET(x) (((PT_REGS_ARM64 )(x))->regs[30])
/ Works only with CONFIG_FRAME_POINTER */
#define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])
#define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])
#define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
#define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)
kretprobe
kretprobe__ kernel_function_name
tracepoint
TRACEPOINT_PROBE(类别,事件)
这是一个检测由category : event定义的跟踪点的宏。
跟踪点名称是<category>:<event>。探测函数名称为tracepoint__<category>__<event>.
参数在args结构中可用,它们是跟踪点参数。列出这些的一种方法是在/sys/kernel/debug/tracing/events/category/event/format 下分类相关格式文件。
该args结构可用于代替ctx需要上下文作为参数的每个函数。
TRACEPOINT_PROBE (random, urandom_read)
args 来自 /sys/kernel/debug/tracing/events/random/urandom_read/format
BCC内置接口
bpf_probe_read_kernel()
这会将 size 字节从内核地址空间复制到 BPF 堆栈,以便 BPF 稍后可以对其进行操作。为了安全起见,所有内核内存读取都必须通过 bpf_probe_read_kernel()。
bpf_probe_read_kernel_str()
这会将一个终止的字符串从内核地址空间复制NULL到 BPF 堆栈,以便 BPF 稍后可以对其进行操作。如果字符串长度小于大小,则不会用更多NULL字节填充目标。如果字符串长度大于大小,size - 1则只复制字节并将最后一个字节设置为NULL.
bpf_ktime_get_ns()
u64 纳秒数。在系统引导时启动,但在挂起期间停止。
bpf_get_current_pid_tgid()
返回低 32 位的进程 ID(内核的 PID 视图,在用户空间中通常表示为线程 ID)和高 32 位的线程组ID(用户空间通常认为 PID) . 通过直接将其设置为 u32,我们丢弃了高 32 位。
bpf_get_current_uid_gid()
返回用户 ID 和组 ID
bpf_get_current_comm()
使用当前进程名称填充第一个参数地址。它应该是一个指向至少大小为 TASK_COMM_LEN 的 char 数组的指针,该数组在 linux/sched.h 中定义。
bpf_get_current_task()
返回指向当前任务的 task_struct 对象的指针。该帮助器可用于计算进程的 CPU 运行时间、识别内核线程、获取当前 CPU 的运行队列或检索许多其他信息。
bpf_log2l()
返回所提供值的 log-2。这通常用于为直方图创建索引,以构建 2 的幂直方图。
bpf_get_prandom_u32()
返回一个伪随机 u32。
bpf_probe_read_user()
这会尝试从用户地址空间安全地读取 size 字节到 BPF 堆栈,以便 BPF 稍后可以对其进行操作。为安全起见,所有用户地址空间内存读取都必须通过 bpf_probe_read_user()。
bpf_probe_read_user_str()
这会将终止的字符串从用户地址空间复制NULL到 BPF 堆栈,以便 BPF 稍后可以对其进行操作。如果字符串长度小于大小,则不会用更多NULL字节填充目标。如果字符串长度大于大小,size - 1则只复制字节并将最后一个字节设置为NULL.
bpf_get_ns_current_pid_tgid()
从当前命名空间看到的pid和tgid的值将在nsdata中返回。
BCC输出
bpf_trace_printk()
用于 printf() 到公共 trace_pipe (/sys/kernel/debug/tracing/trace_pipe) 的简单内核工具。这对于一些简单的示例来说是可以的,但有限制:最多 3 个 args,仅 1 %s,并且 trace_pipe 是全局共享的,因此并发程序会有冲突的输出。更好的接口是通过 BPF_PERF_OUTPUT()。
BPF_PERF_OUTPUT
创建一个 BPF 表,用于通过 perf 环形缓冲区将自定义事件数据推送到用户空间。这是将每个事件数据推送到用户空间的首选方法。
perf_submit()
BPF_PERF_OUTPUT 表的一种方法,用于向用户空间提交自定义事件数据。请参阅BPF_PERF_OUTPUT条目。(这最终会调用 bpf_perf_event_output())
perf_submit_skb()
网络程序类型中可用的 BPF_PERF_OUTPUT 表的一种方法,用于向用户空间提交自定义事件数据,以及packet_size数据包缓冲区的第一个字节。请参阅 BPF_PERF_OUTPUT 条目。(这最终会调用 bpf_perf_event_output()。)
BPF_RINGBUF_OUTPUT
创建一个 BPF 表,用于通过 ringbuf 环形缓冲区将自定义事件数据推送到用户空间。 BPF_RINGBUF_OUTPUT有几个优点BPF_PERF_OUTPUT,总结如下:
- 缓冲区在所有 CPU 之间共享,这意味着没有每个 CPU 分配
- 支持 BPF 程序的两个 API
-
map.ringbuf_output()像map.perf_submit()(在ringbuf_output中涵盖) -
map.ringbuf_reserve()//将预留缓冲区空间map.ringbuf_submit()和map.ringbuf_discard()提交事件的过程分成两步(在ringbuf_reserve,ringbuf_submit,ringbuf_discard中涉及)
-
- BPF API 不需要访问 CPU ctx 参数
- 由于共享环形缓冲区管理器,用户空间的卓越性能和延迟
- 支持两种在用户空间消费数据的方式
从 Linux 5.8 开始,这应该是将每个事件数据推送到用户空间的首选方法。
ringbuf_output()
BPF_RINGBUF_OUTPUT 表的一种方法,用于向用户空间提交自定义事件数据。此方法perf_submit()的工作方式类似于 ,尽管它不需要 ctx 参数。
ringbuf_reserve()
BPF_RINGBUF_OUTPUT 表的一种方法,用于在环形缓冲区中保留空间并同时为输出分配数据结构。必须与ringbuf_submit或之一一起使用ringbuf_discard。
ringbuf_submit()
BPF_RINGBUF_OUTPUT 表的一种方法,用于向用户空间提交自定义事件数据。必须在调用之前 ringbuf_reserve()为数据保留空间。
ringbuf_discard()
BPF_RINGBUF_OUTPUT 表的一种方法,用于丢弃自定义事件数据;用户空间忽略与丢弃事件相关的数据。必须在调用之前 ringbuf_reserve()为数据保留空间。
BCC Maps
BPF_TABLE
创建一个名为_name 的地图。大多数情况下,这将通过更高级别的宏来使用,例如 BPF_HASH、BPF_ARRAY、BPF_HISTOGRAM 等。BPF_F_TABLE是一个变体,它在最后一个参数中带有一个标志。
BPF_HASH
创建一个名为 name的哈希映射(关联数组),带有可选参数。
BPF_ARRAY
创建一个 int 索引数组,该数组针对最快的查找和更新进行了优化,命名为name,带有可选参数。
BPF_HISTOGRAM
创建一个名为name 的直方图,带有可选参数
BPF_STACK_TRACE
创建名为 name的堆栈跟踪映射,并提供最大条目数。这些映射用于存储堆栈跟踪。
BPF_PERF_ARRAY
创建名为name 的 perf 数组,并提供最大条目数,该数必须等于系统 cpu 的数量。这些映射用于获取硬件性能计数器。
BPF_PERCPU_HASH
使用可选参数创建名为name 的 NUM_CPU 整数索引哈希映射(关联数组) 。每个 CPU 将拥有该数组的单独副本。副本不会以任何方式保持同步。
请注意,由于内核中定义的限制(在 linux/mm/percpu.c 中),leaf_type大小不能超过 32KB。换句话说,BPF_PERCPU_HASH元素的大小不能大于 32KB。
BPF_PERCPU_ARRAY
创建 NUM_CPU 整数索引数组,这些数组已针对最快的查找和更新进行了优化,命名为name,带有可选参数。每个 CPU 将拥有该数组的单独副本。副本不会以任何方式保持同步。
请注意,由于内核中定义的限制(在 linux/mm/percpu.c 中),leaf_type大小不能超过 32KB。换句话说,BPF_PERCPU_ARRAY元素的大小不能大于 32KB。
BPF_LPM_TRIE
创建一个名为name 的最长前缀匹配树映射,带有可选参数。
BPF_PROG_ARRAY
这将创建一个name以size条目命名的程序数组。数组的每个条目要么是 bpf 程序的文件描述符,要么是NULL. 该数组充当跳转表,以便 bpf 程序可以“尾调用”其他 bpf 程序。
BPF_DEVMAP
这将创建一个name以size条目命名的设备映射。映射的每个条目都是ifindex一个网络接口。此地图仅在 XDP 中使用。
BPF_CPUMAP
这将创建一个name以size条目命名的 cpu 映射。map 的索引代表 CPU id,每个条目是分配给 CPU 的环形缓冲区的大小。此地图仅在 XDP 中使用。
BPF_XSKMAP
这将创建一个name以size条目命名的 xsk 映射。每个条目代表一个 NIC 的队列 ID。此映射仅在 XDP 中用于将数据包重定向到 AF_XDP 套接字。如果 AF_XDP 套接字绑定到与当前数据包的队列 ID 不同的队列,则该数据包将被丢弃。对于内核 v5.3 及更高版本,lookup方法可用,可用于检查 AF_XDP 套接字是否可用于当前数据包的队列 id。更多细节在AF_XDP。
BPF_ARRAY_OF_MAPS
这将创建一个数组映射,其映射类型 (BPF_MAP_TYPE_HASH_OF_MAPS) 映射以name条目size命名。内部映射元数据由映射提供inner_map_name,可以是除BPF_MAP_TYPE_PROG_ARRAY,BPF_MAP_TYPE_CGROUP_STORAGE和之外的大多数数组或哈希映射BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE。
BPF_HASH_OF_MAPS
这将创建一个哈希映射,其映射类型 (BPF_MAP_TYPE_HASH_OF_MAPS) 映射以name条目size命名。内部映射元数据由映射提供inner_map_name,可以是除BPF_MAP_TYPE_PROG_ARRAY,BPF_MAP_TYPE_CGROUP_STORAGE和之外的大多数数组或哈希映射BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE。
BPF_STACK
创建一个以namevalue typeleaf_type和 max entries命名的堆栈max_entries。
BPF_QUEUE
创建一个以namevalue typeleaf_type和 max entries命名的队列max_entries。
BPF_SOCKHASH
创建一个名为 的哈希name,带有可选参数。
map.lookup()
在映射中查找键,如果存在则返回指向其值的指针,否则返回 NULL。我们将密钥作为地址传递给指针。
map.lookup_or_try_init()
在映射中查找键,如果存在则返回指向其值的指针,否则将键的值初始化为第二个参数。这通常用于将值初始化为零。如果无法插入密钥(例如地图已满),则返回 NULL。
map.delete()
从哈希中删除密钥。
map.update()
将第二个参数中的值与键关联,覆盖之前的任何值。
map.insert()
仅当没有先前的值时,才将第二个参数中的值与键相关联。
map.increment()
将键的值增加increment_amount,默认为 1。用于直方图。
map.get_stackid()
这将遍历通过 struct pt_regs in 找到的堆栈ctx,将其保存在堆栈跟踪映射中,并返回堆栈跟踪的唯一 ID。
map.perf_read()
这将返回5. BPF_PERF_ARRAY中配置的硬件性能计数器
map.call()
这将调用bpf_tail_call()尾调用BPF_PROG_ARRAYindex中的条目指向的 bpf 程序。尾随呼叫与正常呼叫不同。它在跳转到另一个 bpf 程序后重用当前堆栈帧并且永不返回。如果该条目为空,则不会跳转到任何地方,程序继续正常执行。
map.redirect_map()
这会根据index条目重定向传入的数据包。如果映射为BPF_DEVMAP,则数据包将被发送到入口指向的网络接口的传输队列。如果映射为BPF_CPUMAP,则数据包将被发送到 CPU 的环形缓冲区,index稍后由 CPU 处理。如果映射是BPF_XSKMAP,数据包将被发送到附加到队列的 AF_XDP 套接字
map.push()
将元素推送到堆栈或队列表上。将 BPF_EXIST 作为标志传递会导致队列或堆栈在最旧的元素已满时丢弃它。成功返回 0,失败返回负错误。
map.pop()
从 Stack 或 Queue 表中弹出一个元素。*val填充有结果。与偷看不同,弹出会删除元素。成功返回 0,失败返回负错误。
map.peek()
查看 Stack 或 Queue 表头部的元素。*val填充有结果。与弹出不同,窥视不会删除元素。成功返回 0,失败返回负错误。
map.sock_hash_update()
添加条目或更新引用套接字的 sockhash 映射。skops 用作与键关联的条目的新值。如果地图有 eBPF 程序(解析器和判定),这些程序将被添加的套接字继承。如果套接字已经附加到 eBPF 程序,这会导致错误。
map.msg_redirect_hash()
此帮助程序用于在套接字级别实现策略的程序中。如果消息 msg 被允许通过(即如果判决 eBPF 程序返回 SK_PASS),则使用哈希键将其重定向到 map 引用的套接字(类型为 BPF_MAP_TYPE_SOCKHASH)。入口和出口接口都可用于重定向。flags 中的 BPF_F_INGRESS 值用于区分(如果存在标志,则选择入口路径,否则选择出口路径)。这是目前唯一支持的标志。
map.sk_redirect_hash()
此帮助程序用于在 skb 套接字级别实现策略的程序中。如果 sk_buff skb 被允许通过(即如果判决 eBPF 程序返回 SK_PASS),则使用哈希键将其重定向到 map 引用的套接字(类型为BPF_MAP_TYPE_SOCKHASH)。入口和出口接口都可用于重定向。flags 中的 BPF_F_INGRESS 值用于区分(如果存在标志,则选择入口路径,否则选择出口)。这是目前唯一支持的标志