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