描述:

BPF_MAP_TYPE_PERF_EVENT_ARRAY 的使用方式。

bootstrap_legacy 会将环形缓冲区映射(ring buffer maps)替换为性能事件数组(perf event array,对应 BPF 映射类型 BPF_MAP_TYPE_PERF_EVENT_ARRAY),以实现与旧版内核的兼容性。

运行

1
2
3
4
5
6
7
8
$ ./bootstrap_legacy
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
17:26:53 EXEC sh 24694 22237 /bin/sh
17:26:53 EXEC grep 24696 24694 /usr/bin/grep
17:26:53 EXEC ls 24695 24694 /usr/bin/ls
17:26:53 EXIT ls 24695 24694 [2] (5ms)
17:26:53 EXIT grep 24696 24694 [0] (6ms)
17:26:53 EXIT sh 24694 22237 [0] (8ms)

The BPF side

BPF maps

BPF_MAP_TYPE_PERF_EVENT_ARRAY

1
2
3
4
5
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} perf_buffer SEC(".maps");

SEC(“tp/sched/sched_process_exec”)

1
2
SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)

SEC(“tp/sched/sched_process_exit”)

1
2
SEC("tp/sched/sched_process_exit")
int handle_exit(struct trace_event_raw_sched_process_template *ctx)

bpf_perf_event_output

1
2
/* use perf event output to send data to user-space for post-processing */
bpf_perf_event_output(ctx, &perf_buffer, BPF_F_CURRENT_CPU, &e, sizeof(e));

eBPF map 的数据存入索引,有两种索引指定方式:

  1. 手动指定索引并经掩码处理:BPF_F_INDEX_MASK 是 “索引掩码”,用于过滤 / 修正传入的映射索引值

  2. 自动使用当前 CPU 核心索引:BPF_F_CURRENT_CPU 表示 “当前 CPU”,是便捷定位当前运行 CPU 核心索引的预设标志

每个性能事件(perf event)都创建于特定的 CPU 之上。此辅助函数(helper)仅能写入与 eBPF 程序运行在同一 CPU 上的性能事件;若手动指定的索引对应的性能事件位于其他 CPU,运行时会返回 -EOPNOTSUPP 错误。

因此,除非有充分的合理理由,否则建议使用 BPF_F_CURRENT_CPU 标志,并以 “CPU 索引与映射(map)索引保持一致” 的方式填充 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型的映射。

The user-space side

perf_buffer__new

1
2
3
4
5
6
7
/* Set up perf buffer polling */
perf_buffer = perf_buffer__new(bpf_map__fd(skel->maps.perf_buffer), 8, handle_event, NULL, NULL, NULL);
if (!perf_buffer) {
err = -1;
fprintf(stderr, "Failed to create perf event buffer\n");
goto cleanup;
}

8 表示的是 number of memory pages,每个 page 是 4KB,因此总大小: 8 pages x 4096 byte/page = 32KB。即设置 per-CPU buffer 为 32KB。

perf_buffer__poll

1
2
3
4
5
6
7
8
9
10
11
12
while (!exiting) {
err = perf_buffer__poll(perf_buffer, 100 /* timeout, ms */);
/* Ctrl-C will cause -EINTR */
if (err == -EINTR) {
err = 0;
break;
}
if (err < 0) {
printf("Error polling perf buffer: %d\n", err);
break;
}
}

clean_up

1
2
3
4
cleanup:
/* Clean up */
perf_buffer__free(perf_buffer);
bootstrap_legacy_bpf__destroy(skel);