xref: /linux-6.15/tools/perf/util/bpf-filter.c (revision ae4aa00a)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <stdlib.h>
3 
4 #include <bpf/bpf.h>
5 #include <linux/err.h>
6 #include <internal/xyarray.h>
7 
8 #include "util/debug.h"
9 #include "util/evsel.h"
10 
11 #include "util/bpf-filter.h"
12 #include "util/bpf-filter-flex.h"
13 #include "util/bpf-filter-bison.h"
14 
15 #include "bpf_skel/sample-filter.h"
16 #include "bpf_skel/sample_filter.skel.h"
17 
18 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
19 
20 #define __PERF_SAMPLE_TYPE(st, opt)	{ st, #st, opt }
21 #define PERF_SAMPLE_TYPE(_st, opt)	__PERF_SAMPLE_TYPE(PERF_SAMPLE_##_st, opt)
22 
23 static const struct perf_sample_info {
24 	u64 type;
25 	const char *name;
26 	const char *option;
27 } sample_table[] = {
28 	/* default sample flags */
29 	PERF_SAMPLE_TYPE(IP, NULL),
30 	PERF_SAMPLE_TYPE(TID, NULL),
31 	PERF_SAMPLE_TYPE(PERIOD, NULL),
32 	/* flags mostly set by default, but still have options */
33 	PERF_SAMPLE_TYPE(ID, "--sample-identifier"),
34 	PERF_SAMPLE_TYPE(CPU, "--sample-cpu"),
35 	PERF_SAMPLE_TYPE(TIME, "-T"),
36 	/* optional sample flags */
37 	PERF_SAMPLE_TYPE(ADDR, "-d"),
38 	PERF_SAMPLE_TYPE(DATA_SRC, "-d"),
39 	PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"),
40 	PERF_SAMPLE_TYPE(WEIGHT, "-W"),
41 	PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"),
42 	PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"),
43 	PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"),
44 	PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"),
45 };
46 
47 static const struct perf_sample_info *get_sample_info(u64 flags)
48 {
49 	size_t i;
50 
51 	for (i = 0; i < ARRAY_SIZE(sample_table); i++) {
52 		if (sample_table[i].type == flags)
53 			return &sample_table[i];
54 	}
55 	return NULL;
56 }
57 
58 static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr)
59 {
60 	const struct perf_sample_info *info;
61 
62 	if (evsel->core.attr.sample_type & expr->sample_flags)
63 		return 0;
64 
65 	info = get_sample_info(expr->sample_flags);
66 	if (info == NULL) {
67 		pr_err("Error: %s event does not have sample flags %lx\n",
68 		       evsel__name(evsel), expr->sample_flags);
69 		return -1;
70 	}
71 
72 	pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name);
73 	if (info->option)
74 		pr_err(" Hint: please add %s option to perf record\n", info->option);
75 	return -1;
76 }
77 
78 int perf_bpf_filter__prepare(struct evsel *evsel)
79 {
80 	int i, x, y, fd;
81 	struct sample_filter_bpf *skel;
82 	struct bpf_program *prog;
83 	struct bpf_link *link;
84 	struct perf_bpf_filter_expr *expr;
85 
86 	skel = sample_filter_bpf__open_and_load();
87 	if (!skel) {
88 		pr_err("Failed to load perf sample-filter BPF skeleton\n");
89 		return -1;
90 	}
91 
92 	i = 0;
93 	fd = bpf_map__fd(skel->maps.filters);
94 	list_for_each_entry(expr, &evsel->bpf_filters, list) {
95 		struct perf_bpf_filter_entry entry = {
96 			.op = expr->op,
97 			.part = expr->part,
98 			.flags = expr->sample_flags,
99 			.value = expr->val,
100 		};
101 
102 		if (check_sample_flags(evsel, expr) < 0)
103 			return -1;
104 
105 		bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
106 		i++;
107 
108 		if (expr->op == PBF_OP_GROUP_BEGIN) {
109 			struct perf_bpf_filter_expr *group;
110 
111 			list_for_each_entry(group, &expr->groups, list) {
112 				struct perf_bpf_filter_entry group_entry = {
113 					.op = group->op,
114 					.part = group->part,
115 					.flags = group->sample_flags,
116 					.value = group->val,
117 				};
118 				bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY);
119 				i++;
120 			}
121 
122 			memset(&entry, 0, sizeof(entry));
123 			entry.op = PBF_OP_GROUP_END;
124 			bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
125 			i++;
126 		}
127 	}
128 
129 	if (i > MAX_FILTERS) {
130 		pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS);
131 		return -1;
132 	}
133 	prog = skel->progs.perf_sample_filter;
134 	for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
135 		for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
136 			link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
137 			if (IS_ERR(link)) {
138 				pr_err("Failed to attach perf sample-filter program\n");
139 				return PTR_ERR(link);
140 			}
141 		}
142 	}
143 	evsel->bpf_skel = skel;
144 	return 0;
145 }
146 
147 int perf_bpf_filter__destroy(struct evsel *evsel)
148 {
149 	struct perf_bpf_filter_expr *expr, *tmp;
150 
151 	list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
152 		list_del(&expr->list);
153 		free(expr);
154 	}
155 	sample_filter_bpf__destroy(evsel->bpf_skel);
156 	return 0;
157 }
158 
159 u64 perf_bpf_filter__lost_count(struct evsel *evsel)
160 {
161 	struct sample_filter_bpf *skel = evsel->bpf_skel;
162 
163 	return skel ? skel->bss->dropped : 0;
164 }
165 
166 struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part,
167 						       enum perf_bpf_filter_op op,
168 						       unsigned long val)
169 {
170 	struct perf_bpf_filter_expr *expr;
171 
172 	expr = malloc(sizeof(*expr));
173 	if (expr != NULL) {
174 		expr->sample_flags = sample_flags;
175 		expr->part = part;
176 		expr->op = op;
177 		expr->val = val;
178 		INIT_LIST_HEAD(&expr->groups);
179 	}
180 	return expr;
181 }
182 
183 int perf_bpf_filter__parse(struct list_head *expr_head, const char *str)
184 {
185 	YY_BUFFER_STATE buffer;
186 	int ret;
187 
188 	buffer = perf_bpf_filter__scan_string(str);
189 
190 	ret = perf_bpf_filter_parse(expr_head);
191 
192 	perf_bpf_filter__flush_buffer(buffer);
193 	perf_bpf_filter__delete_buffer(buffer);
194 	perf_bpf_filter_lex_destroy();
195 
196 	return ret;
197 }
198