xref: /linux/tools/perf/util/bpf_skel/sample_filter.bpf.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 // Copyright (c) 2023 Google
3 #include "vmlinux.h"
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_tracing.h>
6 #include <bpf/bpf_core_read.h>
7 
8 #include "sample-filter.h"
9 
10 /* BPF map that will be filled by user space */
11 struct filters {
12 	__uint(type, BPF_MAP_TYPE_HASH);
13 	__type(key, int);
14 	__type(value, struct perf_bpf_filter_entry[MAX_FILTERS]);
15 	__uint(max_entries, 1);
16 } filters SEC(".maps");
17 
18 /*
19  * An evsel has multiple instances for each CPU or task but we need a single
20  * id to be used as a key for the idx_hash.  This hashmap would translate the
21  * instance's ID to a representative ID.
22  */
23 struct event_hash {
24 	__uint(type, BPF_MAP_TYPE_HASH);
25 	__type(key, __u64);
26 	__type(value, __u64);
27 	__uint(max_entries, 1);
28 } event_hash SEC(".maps");
29 
30 /* tgid/evtid to filter index */
31 struct idx_hash {
32 	__uint(type, BPF_MAP_TYPE_HASH);
33 	__type(key, struct idx_hash_key);
34 	__type(value, int);
35 	__uint(max_entries, 1);
36 } idx_hash SEC(".maps");
37 
38 /* tgid to filter index */
39 struct lost_count {
40 	__uint(type, BPF_MAP_TYPE_ARRAY);
41 	__type(key, int);
42 	__type(value, int);
43 	__uint(max_entries, 1);
44 } dropped SEC(".maps");
45 
46 volatile const int use_idx_hash;
47 
48 void *bpf_cast_to_kern_ctx(void *) __ksym;
49 
50 /* new kernel perf_sample_data definition */
51 struct perf_sample_data___new {
52 	__u64 sample_flags;
53 } __attribute__((preserve_access_index));
54 
55 /* new kernel perf_mem_data_src definition */
56 union perf_mem_data_src___new {
57 	__u64 val;
58 	struct {
59 		__u64   mem_op:5,	/* type of opcode */
60 			mem_lvl:14,	/* memory hierarchy level */
61 			mem_snoop:5,	/* snoop mode */
62 			mem_lock:2,	/* lock instr */
63 			mem_dtlb:7,	/* tlb access */
64 			mem_lvl_num:4,	/* memory hierarchy level number */
65 			mem_remote:1,   /* remote */
66 			mem_snoopx:2,	/* snoop mode, ext */
67 			mem_blk:3,	/* access blocked */
68 			mem_hops:3,	/* hop level */
69 			mem_rsvd:18;
70 	};
71 };
72 
73 /* helper function to return the given perf sample data */
perf_get_sample(struct bpf_perf_event_data_kern * kctx,struct perf_bpf_filter_entry * entry)74 static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
75 				    struct perf_bpf_filter_entry *entry)
76 {
77 	struct perf_sample_data___new *data = (void *)kctx->data;
78 
79 	if (!bpf_core_field_exists(data->sample_flags))
80 		return 0;
81 
82 #define BUILD_CHECK_SAMPLE(x)					\
83 	_Static_assert((1 << (PBF_TERM_##x - PBF_TERM_SAMPLE_START)) == PERF_SAMPLE_##x, \
84 		"Mismatched PBF term to sample bit " #x)
85 	BUILD_CHECK_SAMPLE(IP);
86 	BUILD_CHECK_SAMPLE(TID);
87 	BUILD_CHECK_SAMPLE(TIME);
88 	BUILD_CHECK_SAMPLE(ADDR);
89 	BUILD_CHECK_SAMPLE(ID);
90 	BUILD_CHECK_SAMPLE(CPU);
91 	BUILD_CHECK_SAMPLE(PERIOD);
92 	BUILD_CHECK_SAMPLE(WEIGHT);
93 	BUILD_CHECK_SAMPLE(DATA_SRC);
94 	BUILD_CHECK_SAMPLE(TRANSACTION);
95 	BUILD_CHECK_SAMPLE(PHYS_ADDR);
96 	BUILD_CHECK_SAMPLE(CGROUP);
97 	BUILD_CHECK_SAMPLE(DATA_PAGE_SIZE);
98 	BUILD_CHECK_SAMPLE(CODE_PAGE_SIZE);
99 	BUILD_CHECK_SAMPLE(WEIGHT_STRUCT);
100 #undef BUILD_CHECK_SAMPLE
101 
102 	/* For sample terms check the sample bit is set. */
103 	if (entry->term >= PBF_TERM_SAMPLE_START && entry->term <= PBF_TERM_SAMPLE_END &&
104 	    (data->sample_flags & (1 << (entry->term - PBF_TERM_SAMPLE_START))) == 0)
105 		return 0;
106 
107 	switch (entry->term) {
108 	case PBF_TERM_IP:
109 		return kctx->data->ip;
110 	case PBF_TERM_ID:
111 		return kctx->data->id;
112 	case PBF_TERM_TID:
113 		if (entry->part)
114 			return kctx->data->tid_entry.pid;
115 		else
116 			return kctx->data->tid_entry.tid;
117 	case PBF_TERM_CPU:
118 		return kctx->data->cpu_entry.cpu;
119 	case PBF_TERM_TIME:
120 		return kctx->data->time;
121 	case PBF_TERM_ADDR:
122 		return kctx->data->addr;
123 	case PBF_TERM_PERIOD:
124 		return kctx->data->period;
125 	case PBF_TERM_TRANSACTION:
126 		return kctx->data->txn;
127 	case PBF_TERM_WEIGHT_STRUCT:
128 		if (entry->part == 1)
129 			return kctx->data->weight.var1_dw;
130 		if (entry->part == 2)
131 			return kctx->data->weight.var2_w;
132 		if (entry->part == 3)
133 			return kctx->data->weight.var3_w;
134 		/* fall through */
135 	case PBF_TERM_WEIGHT:
136 		return kctx->data->weight.full;
137 	case PBF_TERM_PHYS_ADDR:
138 		return kctx->data->phys_addr;
139 	case PBF_TERM_CGROUP:
140 		return kctx->data->cgroup;
141 	case PBF_TERM_CODE_PAGE_SIZE:
142 		return kctx->data->code_page_size;
143 	case PBF_TERM_DATA_PAGE_SIZE:
144 		return kctx->data->data_page_size;
145 	case PBF_TERM_DATA_SRC:
146 		if (entry->part == 1)
147 			return kctx->data->data_src.mem_op;
148 		if (entry->part == 2)
149 			return kctx->data->data_src.mem_lvl_num;
150 		if (entry->part == 3) {
151 			__u32 snoop = kctx->data->data_src.mem_snoop;
152 			__u32 snoopx = kctx->data->data_src.mem_snoopx;
153 
154 			return (snoopx << 5) | snoop;
155 		}
156 		if (entry->part == 4)
157 			return kctx->data->data_src.mem_remote;
158 		if (entry->part == 5)
159 			return kctx->data->data_src.mem_lock;
160 		if (entry->part == 6)
161 			return kctx->data->data_src.mem_dtlb;
162 		if (entry->part == 7)
163 			return kctx->data->data_src.mem_blk;
164 		if (entry->part == 8) {
165 			union perf_mem_data_src___new *data = (void *)&kctx->data->data_src;
166 
167 			if (bpf_core_field_exists(data->mem_hops))
168 				return data->mem_hops;
169 
170 			return 0;
171 		}
172 		/* return the whole word */
173 		return kctx->data->data_src.val;
174 	case PBF_TERM_UID:
175 		return bpf_get_current_uid_gid() & 0xFFFFFFFF;
176 	case PBF_TERM_GID:
177 		return bpf_get_current_uid_gid() >> 32;
178 	case PBF_TERM_NONE:
179 	case __PBF_UNUSED_TERM4:
180 	case __PBF_UNUSED_TERM5:
181 	case __PBF_UNUSED_TERM9:
182 	case __PBF_UNUSED_TERM10:
183 	case __PBF_UNUSED_TERM11:
184 	case __PBF_UNUSED_TERM12:
185 	case __PBF_UNUSED_TERM13:
186 	case __PBF_UNUSED_TERM16:
187 	case __PBF_UNUSED_TERM18:
188 	case __PBF_UNUSED_TERM20:
189 	default:
190 		break;
191 	}
192 	return 0;
193 }
194 
195 #define CHECK_RESULT(data, op, val)			\
196 	if (!(data op val)) {				\
197 		if (!in_group)				\
198 			goto drop;			\
199 	} else if (in_group) {				\
200 		group_result = 1;			\
201 	}
202 
203 /* BPF program to be called from perf event overflow handler */
204 SEC("perf_event")
perf_sample_filter(void * ctx)205 int perf_sample_filter(void *ctx)
206 {
207 	struct bpf_perf_event_data_kern *kctx;
208 	struct perf_bpf_filter_entry *entry;
209 	__u64 sample_data;
210 	int in_group = 0;
211 	int group_result = 0;
212 	int i, k;
213 	int *losts;
214 
215 	kctx = bpf_cast_to_kern_ctx(ctx);
216 
217 	k = 0;
218 
219 	if (use_idx_hash) {
220 		struct idx_hash_key key = {
221 			.tgid = bpf_get_current_pid_tgid() >> 32,
222 		};
223 		__u64 eid = kctx->event->id;
224 		__u64 *key_id;
225 		int *idx;
226 
227 		/* get primary_event_id */
228 		if (kctx->event->parent)
229 			eid = kctx->event->parent->id;
230 
231 		key_id = bpf_map_lookup_elem(&event_hash, &eid);
232 		if (key_id == NULL)
233 			goto drop;
234 
235 		key.evt_id = *key_id;
236 
237 		idx = bpf_map_lookup_elem(&idx_hash, &key);
238 		if (idx)
239 			k = *idx;
240 		else
241 			goto drop;
242 	}
243 
244 	entry = bpf_map_lookup_elem(&filters, &k);
245 	if (entry == NULL)
246 		goto drop;
247 
248 	for (i = 0; i < MAX_FILTERS; i++) {
249 		sample_data = perf_get_sample(kctx, &entry[i]);
250 
251 		switch (entry[i].op) {
252 		case PBF_OP_EQ:
253 			CHECK_RESULT(sample_data, ==, entry[i].value)
254 			break;
255 		case PBF_OP_NEQ:
256 			CHECK_RESULT(sample_data, !=, entry[i].value)
257 			break;
258 		case PBF_OP_GT:
259 			CHECK_RESULT(sample_data, >, entry[i].value)
260 			break;
261 		case PBF_OP_GE:
262 			CHECK_RESULT(sample_data, >=, entry[i].value)
263 			break;
264 		case PBF_OP_LT:
265 			CHECK_RESULT(sample_data, <, entry[i].value)
266 			break;
267 		case PBF_OP_LE:
268 			CHECK_RESULT(sample_data, <=, entry[i].value)
269 			break;
270 		case PBF_OP_AND:
271 			CHECK_RESULT(sample_data, &, entry[i].value)
272 			break;
273 		case PBF_OP_GROUP_BEGIN:
274 			in_group = 1;
275 			group_result = 0;
276 			break;
277 		case PBF_OP_GROUP_END:
278 			if (group_result == 0)
279 				goto drop;
280 			in_group = 0;
281 			break;
282 		case PBF_OP_DONE:
283 			/* no failures so far, accept it */
284 			return 1;
285 		}
286 	}
287 	/* generate sample data */
288 	return 1;
289 
290 drop:
291 	losts = bpf_map_lookup_elem(&dropped, &k);
292 	if (losts != NULL)
293 		__sync_fetch_and_add(losts, 1);
294 
295 	return 0;
296 }
297 
298 char LICENSE[] SEC("license") = "Dual BSD/GPL";
299