xref: /openbmc/linux/tools/perf/util/bpf-filter.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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  
get_sample_info(u64 flags)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  
check_sample_flags(struct evsel * evsel,struct perf_bpf_filter_expr * expr)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  	if (expr->op == PBF_OP_GROUP_BEGIN) {
66  		struct perf_bpf_filter_expr *group;
67  
68  		list_for_each_entry(group, &expr->groups, list) {
69  			if (check_sample_flags(evsel, group) < 0)
70  				return -1;
71  		}
72  		return 0;
73  	}
74  
75  	info = get_sample_info(expr->sample_flags);
76  	if (info == NULL) {
77  		pr_err("Error: %s event does not have sample flags %lx\n",
78  		       evsel__name(evsel), expr->sample_flags);
79  		return -1;
80  	}
81  
82  	pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name);
83  	if (info->option)
84  		pr_err(" Hint: please add %s option to perf record\n", info->option);
85  	return -1;
86  }
87  
perf_bpf_filter__prepare(struct evsel * evsel)88  int perf_bpf_filter__prepare(struct evsel *evsel)
89  {
90  	int i, x, y, fd;
91  	struct sample_filter_bpf *skel;
92  	struct bpf_program *prog;
93  	struct bpf_link *link;
94  	struct perf_bpf_filter_expr *expr;
95  
96  	skel = sample_filter_bpf__open_and_load();
97  	if (!skel) {
98  		pr_err("Failed to load perf sample-filter BPF skeleton\n");
99  		return -1;
100  	}
101  
102  	i = 0;
103  	fd = bpf_map__fd(skel->maps.filters);
104  	list_for_each_entry(expr, &evsel->bpf_filters, list) {
105  		struct perf_bpf_filter_entry entry = {
106  			.op = expr->op,
107  			.part = expr->part,
108  			.flags = expr->sample_flags,
109  			.value = expr->val,
110  		};
111  
112  		if (check_sample_flags(evsel, expr) < 0)
113  			return -1;
114  
115  		bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
116  		i++;
117  
118  		if (expr->op == PBF_OP_GROUP_BEGIN) {
119  			struct perf_bpf_filter_expr *group;
120  
121  			list_for_each_entry(group, &expr->groups, list) {
122  				struct perf_bpf_filter_entry group_entry = {
123  					.op = group->op,
124  					.part = group->part,
125  					.flags = group->sample_flags,
126  					.value = group->val,
127  				};
128  				bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY);
129  				i++;
130  			}
131  
132  			memset(&entry, 0, sizeof(entry));
133  			entry.op = PBF_OP_GROUP_END;
134  			bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
135  			i++;
136  		}
137  	}
138  
139  	if (i > MAX_FILTERS) {
140  		pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS);
141  		return -1;
142  	}
143  	prog = skel->progs.perf_sample_filter;
144  	for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
145  		for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
146  			link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
147  			if (IS_ERR(link)) {
148  				pr_err("Failed to attach perf sample-filter program\n");
149  				return PTR_ERR(link);
150  			}
151  		}
152  	}
153  	evsel->bpf_skel = skel;
154  	return 0;
155  }
156  
perf_bpf_filter__destroy(struct evsel * evsel)157  int perf_bpf_filter__destroy(struct evsel *evsel)
158  {
159  	struct perf_bpf_filter_expr *expr, *tmp;
160  
161  	list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
162  		list_del(&expr->list);
163  		free(expr);
164  	}
165  	sample_filter_bpf__destroy(evsel->bpf_skel);
166  	return 0;
167  }
168  
perf_bpf_filter__lost_count(struct evsel * evsel)169  u64 perf_bpf_filter__lost_count(struct evsel *evsel)
170  {
171  	struct sample_filter_bpf *skel = evsel->bpf_skel;
172  
173  	return skel ? skel->bss->dropped : 0;
174  }
175  
perf_bpf_filter_expr__new(unsigned long sample_flags,int part,enum perf_bpf_filter_op op,unsigned long val)176  struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part,
177  						       enum perf_bpf_filter_op op,
178  						       unsigned long val)
179  {
180  	struct perf_bpf_filter_expr *expr;
181  
182  	expr = malloc(sizeof(*expr));
183  	if (expr != NULL) {
184  		expr->sample_flags = sample_flags;
185  		expr->part = part;
186  		expr->op = op;
187  		expr->val = val;
188  		INIT_LIST_HEAD(&expr->groups);
189  	}
190  	return expr;
191  }
192  
perf_bpf_filter__parse(struct list_head * expr_head,const char * str)193  int perf_bpf_filter__parse(struct list_head *expr_head, const char *str)
194  {
195  	YY_BUFFER_STATE buffer;
196  	int ret;
197  
198  	buffer = perf_bpf_filter__scan_string(str);
199  
200  	ret = perf_bpf_filter_parse(expr_head);
201  
202  	perf_bpf_filter__flush_buffer(buffer);
203  	perf_bpf_filter__delete_buffer(buffer);
204  	perf_bpf_filter_lex_destroy();
205  
206  	return ret;
207  }
208