xref: /openbmc/linux/tools/perf/util/bpf-filter.c (revision dc7f01f1)
1990a71e9SNamhyung Kim /* SPDX-License-Identifier: GPL-2.0 */
2990a71e9SNamhyung Kim #include <stdlib.h>
3990a71e9SNamhyung Kim 
456ec9457SNamhyung Kim #include <bpf/bpf.h>
556ec9457SNamhyung Kim #include <linux/err.h>
656ec9457SNamhyung Kim #include <internal/xyarray.h>
756ec9457SNamhyung Kim 
856ec9457SNamhyung Kim #include "util/debug.h"
956ec9457SNamhyung Kim #include "util/evsel.h"
1056ec9457SNamhyung Kim 
11990a71e9SNamhyung Kim #include "util/bpf-filter.h"
12c7e97f21SNamhyung Kim #include <util/bpf-filter-flex.h>
13c7e97f21SNamhyung Kim #include <util/bpf-filter-bison.h>
14990a71e9SNamhyung Kim 
1556ec9457SNamhyung Kim #include "bpf_skel/sample-filter.h"
1656ec9457SNamhyung Kim #include "bpf_skel/sample_filter.skel.h"
1756ec9457SNamhyung Kim 
1856ec9457SNamhyung Kim #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
1956ec9457SNamhyung Kim 
204310551bSNamhyung Kim #define __PERF_SAMPLE_TYPE(st, opt)	{ st, #st, opt }
214310551bSNamhyung Kim #define PERF_SAMPLE_TYPE(_st, opt)	__PERF_SAMPLE_TYPE(PERF_SAMPLE_##_st, opt)
224310551bSNamhyung Kim 
234310551bSNamhyung Kim static const struct perf_sample_info {
244310551bSNamhyung Kim 	u64 type;
254310551bSNamhyung Kim 	const char *name;
264310551bSNamhyung Kim 	const char *option;
274310551bSNamhyung Kim } sample_table[] = {
284310551bSNamhyung Kim 	/* default sample flags */
294310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(IP, NULL),
304310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(TID, NULL),
314310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(PERIOD, NULL),
324310551bSNamhyung Kim 	/* flags mostly set by default, but still have options */
334310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(ID, "--sample-identifier"),
344310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(CPU, "--sample-cpu"),
354310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(TIME, "-T"),
364310551bSNamhyung Kim 	/* optional sample flags */
374310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(ADDR, "-d"),
384310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(DATA_SRC, "-d"),
394310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"),
404310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(WEIGHT, "-W"),
414310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"),
424310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"),
434310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"),
444310551bSNamhyung Kim 	PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"),
454310551bSNamhyung Kim };
464310551bSNamhyung Kim 
get_sample_info(u64 flags)474310551bSNamhyung Kim static const struct perf_sample_info *get_sample_info(u64 flags)
484310551bSNamhyung Kim {
494310551bSNamhyung Kim 	size_t i;
504310551bSNamhyung Kim 
514310551bSNamhyung Kim 	for (i = 0; i < ARRAY_SIZE(sample_table); i++) {
524310551bSNamhyung Kim 		if (sample_table[i].type == flags)
534310551bSNamhyung Kim 			return &sample_table[i];
544310551bSNamhyung Kim 	}
554310551bSNamhyung Kim 	return NULL;
564310551bSNamhyung Kim }
574310551bSNamhyung Kim 
check_sample_flags(struct evsel * evsel,struct perf_bpf_filter_expr * expr)584310551bSNamhyung Kim static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr)
594310551bSNamhyung Kim {
604310551bSNamhyung Kim 	const struct perf_sample_info *info;
614310551bSNamhyung Kim 
624310551bSNamhyung Kim 	if (evsel->core.attr.sample_type & expr->sample_flags)
634310551bSNamhyung Kim 		return 0;
644310551bSNamhyung Kim 
65*dc7f01f1SNamhyung Kim 	if (expr->op == PBF_OP_GROUP_BEGIN) {
66*dc7f01f1SNamhyung Kim 		struct perf_bpf_filter_expr *group;
67*dc7f01f1SNamhyung Kim 
68*dc7f01f1SNamhyung Kim 		list_for_each_entry(group, &expr->groups, list) {
69*dc7f01f1SNamhyung Kim 			if (check_sample_flags(evsel, group) < 0)
70*dc7f01f1SNamhyung Kim 				return -1;
71*dc7f01f1SNamhyung Kim 		}
72*dc7f01f1SNamhyung Kim 		return 0;
73*dc7f01f1SNamhyung Kim 	}
74*dc7f01f1SNamhyung Kim 
754310551bSNamhyung Kim 	info = get_sample_info(expr->sample_flags);
764310551bSNamhyung Kim 	if (info == NULL) {
774310551bSNamhyung Kim 		pr_err("Error: %s event does not have sample flags %lx\n",
784310551bSNamhyung Kim 		       evsel__name(evsel), expr->sample_flags);
794310551bSNamhyung Kim 		return -1;
804310551bSNamhyung Kim 	}
814310551bSNamhyung Kim 
824310551bSNamhyung Kim 	pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name);
834310551bSNamhyung Kim 	if (info->option)
844310551bSNamhyung Kim 		pr_err(" Hint: please add %s option to perf record\n", info->option);
854310551bSNamhyung Kim 	return -1;
864310551bSNamhyung Kim }
874310551bSNamhyung Kim 
perf_bpf_filter__prepare(struct evsel * evsel)8856ec9457SNamhyung Kim int perf_bpf_filter__prepare(struct evsel *evsel)
8956ec9457SNamhyung Kim {
9056ec9457SNamhyung Kim 	int i, x, y, fd;
9156ec9457SNamhyung Kim 	struct sample_filter_bpf *skel;
9256ec9457SNamhyung Kim 	struct bpf_program *prog;
9356ec9457SNamhyung Kim 	struct bpf_link *link;
9456ec9457SNamhyung Kim 	struct perf_bpf_filter_expr *expr;
9556ec9457SNamhyung Kim 
9656ec9457SNamhyung Kim 	skel = sample_filter_bpf__open_and_load();
9756ec9457SNamhyung Kim 	if (!skel) {
9856ec9457SNamhyung Kim 		pr_err("Failed to load perf sample-filter BPF skeleton\n");
9956ec9457SNamhyung Kim 		return -1;
10056ec9457SNamhyung Kim 	}
10156ec9457SNamhyung Kim 
10256ec9457SNamhyung Kim 	i = 0;
10356ec9457SNamhyung Kim 	fd = bpf_map__fd(skel->maps.filters);
10456ec9457SNamhyung Kim 	list_for_each_entry(expr, &evsel->bpf_filters, list) {
10556ec9457SNamhyung Kim 		struct perf_bpf_filter_entry entry = {
10656ec9457SNamhyung Kim 			.op = expr->op,
10733581847SNamhyung Kim 			.part = expr->part,
10856ec9457SNamhyung Kim 			.flags = expr->sample_flags,
10956ec9457SNamhyung Kim 			.value = expr->val,
11056ec9457SNamhyung Kim 		};
1114310551bSNamhyung Kim 
1124310551bSNamhyung Kim 		if (check_sample_flags(evsel, expr) < 0)
1134310551bSNamhyung Kim 			return -1;
1144310551bSNamhyung Kim 
11556ec9457SNamhyung Kim 		bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
11656ec9457SNamhyung Kim 		i++;
11746996dd7SNamhyung Kim 
11846996dd7SNamhyung Kim 		if (expr->op == PBF_OP_GROUP_BEGIN) {
11946996dd7SNamhyung Kim 			struct perf_bpf_filter_expr *group;
12046996dd7SNamhyung Kim 
12146996dd7SNamhyung Kim 			list_for_each_entry(group, &expr->groups, list) {
12246996dd7SNamhyung Kim 				struct perf_bpf_filter_entry group_entry = {
12346996dd7SNamhyung Kim 					.op = group->op,
12446996dd7SNamhyung Kim 					.part = group->part,
12546996dd7SNamhyung Kim 					.flags = group->sample_flags,
12646996dd7SNamhyung Kim 					.value = group->val,
12746996dd7SNamhyung Kim 				};
12846996dd7SNamhyung Kim 				bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY);
12946996dd7SNamhyung Kim 				i++;
13056ec9457SNamhyung Kim 			}
13156ec9457SNamhyung Kim 
13246996dd7SNamhyung Kim 			memset(&entry, 0, sizeof(entry));
13346996dd7SNamhyung Kim 			entry.op = PBF_OP_GROUP_END;
13446996dd7SNamhyung Kim 			bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
13546996dd7SNamhyung Kim 			i++;
13646996dd7SNamhyung Kim 		}
13746996dd7SNamhyung Kim 	}
13846996dd7SNamhyung Kim 
13946996dd7SNamhyung Kim 	if (i > MAX_FILTERS) {
14046996dd7SNamhyung Kim 		pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS);
14146996dd7SNamhyung Kim 		return -1;
14246996dd7SNamhyung Kim 	}
14356ec9457SNamhyung Kim 	prog = skel->progs.perf_sample_filter;
14456ec9457SNamhyung Kim 	for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
14556ec9457SNamhyung Kim 		for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
14656ec9457SNamhyung Kim 			link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
14756ec9457SNamhyung Kim 			if (IS_ERR(link)) {
14856ec9457SNamhyung Kim 				pr_err("Failed to attach perf sample-filter program\n");
14956ec9457SNamhyung Kim 				return PTR_ERR(link);
15056ec9457SNamhyung Kim 			}
15156ec9457SNamhyung Kim 		}
15256ec9457SNamhyung Kim 	}
15356ec9457SNamhyung Kim 	evsel->bpf_skel = skel;
15456ec9457SNamhyung Kim 	return 0;
15556ec9457SNamhyung Kim }
15656ec9457SNamhyung Kim 
perf_bpf_filter__destroy(struct evsel * evsel)15756ec9457SNamhyung Kim int perf_bpf_filter__destroy(struct evsel *evsel)
15856ec9457SNamhyung Kim {
15956ec9457SNamhyung Kim 	struct perf_bpf_filter_expr *expr, *tmp;
16056ec9457SNamhyung Kim 
16156ec9457SNamhyung Kim 	list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
16256ec9457SNamhyung Kim 		list_del(&expr->list);
16356ec9457SNamhyung Kim 		free(expr);
16456ec9457SNamhyung Kim 	}
16556ec9457SNamhyung Kim 	sample_filter_bpf__destroy(evsel->bpf_skel);
16656ec9457SNamhyung Kim 	return 0;
16756ec9457SNamhyung Kim }
16856ec9457SNamhyung Kim 
perf_bpf_filter__lost_count(struct evsel * evsel)16927c6f245SNamhyung Kim u64 perf_bpf_filter__lost_count(struct evsel *evsel)
17027c6f245SNamhyung Kim {
17127c6f245SNamhyung Kim 	struct sample_filter_bpf *skel = evsel->bpf_skel;
17227c6f245SNamhyung Kim 
17327c6f245SNamhyung Kim 	return skel ? skel->bss->dropped : 0;
17427c6f245SNamhyung Kim }
17527c6f245SNamhyung Kim 
perf_bpf_filter_expr__new(unsigned long sample_flags,int part,enum perf_bpf_filter_op op,unsigned long val)17633581847SNamhyung Kim struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part,
177990a71e9SNamhyung Kim 						       enum perf_bpf_filter_op op,
178990a71e9SNamhyung Kim 						       unsigned long val)
179990a71e9SNamhyung Kim {
180990a71e9SNamhyung Kim 	struct perf_bpf_filter_expr *expr;
181990a71e9SNamhyung Kim 
182990a71e9SNamhyung Kim 	expr = malloc(sizeof(*expr));
183990a71e9SNamhyung Kim 	if (expr != NULL) {
184990a71e9SNamhyung Kim 		expr->sample_flags = sample_flags;
18533581847SNamhyung Kim 		expr->part = part;
186990a71e9SNamhyung Kim 		expr->op = op;
187990a71e9SNamhyung Kim 		expr->val = val;
18846996dd7SNamhyung Kim 		INIT_LIST_HEAD(&expr->groups);
189990a71e9SNamhyung Kim 	}
190990a71e9SNamhyung Kim 	return expr;
191990a71e9SNamhyung Kim }
192990a71e9SNamhyung Kim 
perf_bpf_filter__parse(struct list_head * expr_head,const char * str)193990a71e9SNamhyung Kim int perf_bpf_filter__parse(struct list_head *expr_head, const char *str)
194990a71e9SNamhyung Kim {
195990a71e9SNamhyung Kim 	YY_BUFFER_STATE buffer;
196990a71e9SNamhyung Kim 	int ret;
197990a71e9SNamhyung Kim 
198990a71e9SNamhyung Kim 	buffer = perf_bpf_filter__scan_string(str);
199990a71e9SNamhyung Kim 
200990a71e9SNamhyung Kim 	ret = perf_bpf_filter_parse(expr_head);
201990a71e9SNamhyung Kim 
202990a71e9SNamhyung Kim 	perf_bpf_filter__flush_buffer(buffer);
203990a71e9SNamhyung Kim 	perf_bpf_filter__delete_buffer(buffer);
204990a71e9SNamhyung Kim 	perf_bpf_filter_lex_destroy();
205990a71e9SNamhyung Kim 
206990a71e9SNamhyung Kim 	return ret;
207990a71e9SNamhyung Kim }
208