1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
3 #include "bench.h"
4 #include "trigger_bench.skel.h"
5 #include "trace_helpers.h"
6 
7 /* BPF triggering benchmarks */
8 static struct trigger_ctx {
9 	struct trigger_bench *skel;
10 } ctx;
11 
12 static struct counter base_hits;
13 
14 static void trigger_validate(void)
15 {
16 	if (env.consumer_cnt != 1) {
17 		fprintf(stderr, "benchmark doesn't support multi-consumer!\n");
18 		exit(1);
19 	}
20 }
21 
22 static void *trigger_base_producer(void *input)
23 {
24 	while (true) {
25 		(void)syscall(__NR_getpgid);
26 		atomic_inc(&base_hits.value);
27 	}
28 	return NULL;
29 }
30 
31 static void trigger_base_measure(struct bench_res *res)
32 {
33 	res->hits = atomic_swap(&base_hits.value, 0);
34 }
35 
36 static void *trigger_producer(void *input)
37 {
38 	while (true)
39 		(void)syscall(__NR_getpgid);
40 	return NULL;
41 }
42 
43 static void trigger_measure(struct bench_res *res)
44 {
45 	res->hits = atomic_swap(&ctx.skel->bss->hits, 0);
46 }
47 
48 static void setup_ctx(void)
49 {
50 	setup_libbpf();
51 
52 	ctx.skel = trigger_bench__open_and_load();
53 	if (!ctx.skel) {
54 		fprintf(stderr, "failed to open skeleton\n");
55 		exit(1);
56 	}
57 }
58 
59 static void attach_bpf(struct bpf_program *prog)
60 {
61 	struct bpf_link *link;
62 
63 	link = bpf_program__attach(prog);
64 	if (!link) {
65 		fprintf(stderr, "failed to attach program!\n");
66 		exit(1);
67 	}
68 }
69 
70 static void trigger_tp_setup(void)
71 {
72 	setup_ctx();
73 	attach_bpf(ctx.skel->progs.bench_trigger_tp);
74 }
75 
76 static void trigger_rawtp_setup(void)
77 {
78 	setup_ctx();
79 	attach_bpf(ctx.skel->progs.bench_trigger_raw_tp);
80 }
81 
82 static void trigger_kprobe_setup(void)
83 {
84 	setup_ctx();
85 	attach_bpf(ctx.skel->progs.bench_trigger_kprobe);
86 }
87 
88 static void trigger_fentry_setup(void)
89 {
90 	setup_ctx();
91 	attach_bpf(ctx.skel->progs.bench_trigger_fentry);
92 }
93 
94 static void trigger_fentry_sleep_setup(void)
95 {
96 	setup_ctx();
97 	attach_bpf(ctx.skel->progs.bench_trigger_fentry_sleep);
98 }
99 
100 static void trigger_fmodret_setup(void)
101 {
102 	setup_ctx();
103 	attach_bpf(ctx.skel->progs.bench_trigger_fmodret);
104 }
105 
106 static void *trigger_consumer(void *input)
107 {
108 	return NULL;
109 }
110 
111 /* make sure call is not inlined and not avoided by compiler, so __weak and
112  * inline asm volatile in the body of the function
113  *
114  * There is a performance difference between uprobing at nop location vs other
115  * instructions. So use two different targets, one of which starts with nop
116  * and another doesn't.
117  *
118  * GCC doesn't generate stack setup preample for these functions due to them
119  * having no input arguments and doing nothing in the body.
120  */
121 __weak void uprobe_target_with_nop(void)
122 {
123 	asm volatile ("nop");
124 }
125 
126 __weak void uprobe_target_without_nop(void)
127 {
128 	asm volatile ("");
129 }
130 
131 static void *uprobe_base_producer(void *input)
132 {
133 	while (true) {
134 		uprobe_target_with_nop();
135 		atomic_inc(&base_hits.value);
136 	}
137 	return NULL;
138 }
139 
140 static void *uprobe_producer_with_nop(void *input)
141 {
142 	while (true)
143 		uprobe_target_with_nop();
144 	return NULL;
145 }
146 
147 static void *uprobe_producer_without_nop(void *input)
148 {
149 	while (true)
150 		uprobe_target_without_nop();
151 	return NULL;
152 }
153 
154 static void usetup(bool use_retprobe, bool use_nop)
155 {
156 	size_t uprobe_offset;
157 	ssize_t base_addr;
158 	struct bpf_link *link;
159 
160 	setup_libbpf();
161 
162 	ctx.skel = trigger_bench__open_and_load();
163 	if (!ctx.skel) {
164 		fprintf(stderr, "failed to open skeleton\n");
165 		exit(1);
166 	}
167 
168 	base_addr = get_base_addr();
169 	if (use_nop)
170 		uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop, base_addr);
171 	else
172 		uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop, base_addr);
173 
174 	link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe,
175 					  use_retprobe,
176 					  -1 /* all PIDs */,
177 					  "/proc/self/exe",
178 					  uprobe_offset);
179 	if (!link) {
180 		fprintf(stderr, "failed to attach uprobe!\n");
181 		exit(1);
182 	}
183 	ctx.skel->links.bench_trigger_uprobe = link;
184 }
185 
186 static void uprobe_setup_with_nop(void)
187 {
188 	usetup(false, true);
189 }
190 
191 static void uretprobe_setup_with_nop(void)
192 {
193 	usetup(true, true);
194 }
195 
196 static void uprobe_setup_without_nop(void)
197 {
198 	usetup(false, false);
199 }
200 
201 static void uretprobe_setup_without_nop(void)
202 {
203 	usetup(true, false);
204 }
205 
206 const struct bench bench_trig_base = {
207 	.name = "trig-base",
208 	.validate = trigger_validate,
209 	.producer_thread = trigger_base_producer,
210 	.consumer_thread = trigger_consumer,
211 	.measure = trigger_base_measure,
212 	.report_progress = hits_drops_report_progress,
213 	.report_final = hits_drops_report_final,
214 };
215 
216 const struct bench bench_trig_tp = {
217 	.name = "trig-tp",
218 	.validate = trigger_validate,
219 	.setup = trigger_tp_setup,
220 	.producer_thread = trigger_producer,
221 	.consumer_thread = trigger_consumer,
222 	.measure = trigger_measure,
223 	.report_progress = hits_drops_report_progress,
224 	.report_final = hits_drops_report_final,
225 };
226 
227 const struct bench bench_trig_rawtp = {
228 	.name = "trig-rawtp",
229 	.validate = trigger_validate,
230 	.setup = trigger_rawtp_setup,
231 	.producer_thread = trigger_producer,
232 	.consumer_thread = trigger_consumer,
233 	.measure = trigger_measure,
234 	.report_progress = hits_drops_report_progress,
235 	.report_final = hits_drops_report_final,
236 };
237 
238 const struct bench bench_trig_kprobe = {
239 	.name = "trig-kprobe",
240 	.validate = trigger_validate,
241 	.setup = trigger_kprobe_setup,
242 	.producer_thread = trigger_producer,
243 	.consumer_thread = trigger_consumer,
244 	.measure = trigger_measure,
245 	.report_progress = hits_drops_report_progress,
246 	.report_final = hits_drops_report_final,
247 };
248 
249 const struct bench bench_trig_fentry = {
250 	.name = "trig-fentry",
251 	.validate = trigger_validate,
252 	.setup = trigger_fentry_setup,
253 	.producer_thread = trigger_producer,
254 	.consumer_thread = trigger_consumer,
255 	.measure = trigger_measure,
256 	.report_progress = hits_drops_report_progress,
257 	.report_final = hits_drops_report_final,
258 };
259 
260 const struct bench bench_trig_fentry_sleep = {
261 	.name = "trig-fentry-sleep",
262 	.validate = trigger_validate,
263 	.setup = trigger_fentry_sleep_setup,
264 	.producer_thread = trigger_producer,
265 	.consumer_thread = trigger_consumer,
266 	.measure = trigger_measure,
267 	.report_progress = hits_drops_report_progress,
268 	.report_final = hits_drops_report_final,
269 };
270 
271 const struct bench bench_trig_fmodret = {
272 	.name = "trig-fmodret",
273 	.validate = trigger_validate,
274 	.setup = trigger_fmodret_setup,
275 	.producer_thread = trigger_producer,
276 	.consumer_thread = trigger_consumer,
277 	.measure = trigger_measure,
278 	.report_progress = hits_drops_report_progress,
279 	.report_final = hits_drops_report_final,
280 };
281 
282 const struct bench bench_trig_uprobe_base = {
283 	.name = "trig-uprobe-base",
284 	.setup = NULL, /* no uprobe/uretprobe is attached */
285 	.producer_thread = uprobe_base_producer,
286 	.consumer_thread = trigger_consumer,
287 	.measure = trigger_base_measure,
288 	.report_progress = hits_drops_report_progress,
289 	.report_final = hits_drops_report_final,
290 };
291 
292 const struct bench bench_trig_uprobe_with_nop = {
293 	.name = "trig-uprobe-with-nop",
294 	.setup = uprobe_setup_with_nop,
295 	.producer_thread = uprobe_producer_with_nop,
296 	.consumer_thread = trigger_consumer,
297 	.measure = trigger_measure,
298 	.report_progress = hits_drops_report_progress,
299 	.report_final = hits_drops_report_final,
300 };
301 
302 const struct bench bench_trig_uretprobe_with_nop = {
303 	.name = "trig-uretprobe-with-nop",
304 	.setup = uretprobe_setup_with_nop,
305 	.producer_thread = uprobe_producer_with_nop,
306 	.consumer_thread = trigger_consumer,
307 	.measure = trigger_measure,
308 	.report_progress = hits_drops_report_progress,
309 	.report_final = hits_drops_report_final,
310 };
311 
312 const struct bench bench_trig_uprobe_without_nop = {
313 	.name = "trig-uprobe-without-nop",
314 	.setup = uprobe_setup_without_nop,
315 	.producer_thread = uprobe_producer_without_nop,
316 	.consumer_thread = trigger_consumer,
317 	.measure = trigger_measure,
318 	.report_progress = hits_drops_report_progress,
319 	.report_final = hits_drops_report_final,
320 };
321 
322 const struct bench bench_trig_uretprobe_without_nop = {
323 	.name = "trig-uretprobe-without-nop",
324 	.setup = uretprobe_setup_without_nop,
325 	.producer_thread = uprobe_producer_without_nop,
326 	.consumer_thread = trigger_consumer,
327 	.measure = trigger_measure,
328 	.report_progress = hits_drops_report_progress,
329 	.report_final = hits_drops_report_final,
330 };
331