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 != 1) {
17 fprintf(stderr, "benchmark doesn't support multi-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
trigger_consumer(void * input)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 */
uprobe_target_with_nop(void)121 __weak void uprobe_target_with_nop(void)
122 {
123 asm volatile ("nop");
124 }
125
uprobe_target_without_nop(void)126 __weak void uprobe_target_without_nop(void)
127 {
128 asm volatile ("");
129 }
130
uprobe_base_producer(void * input)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
uprobe_producer_with_nop(void * input)140 static void *uprobe_producer_with_nop(void *input)
141 {
142 while (true)
143 uprobe_target_with_nop();
144 return NULL;
145 }
146
uprobe_producer_without_nop(void * input)147 static void *uprobe_producer_without_nop(void *input)
148 {
149 while (true)
150 uprobe_target_without_nop();
151 return NULL;
152 }
153
usetup(bool use_retprobe,bool use_nop)154 static void usetup(bool use_retprobe, bool use_nop)
155 {
156 size_t uprobe_offset;
157 struct bpf_link *link;
158
159 setup_libbpf();
160
161 ctx.skel = trigger_bench__open_and_load();
162 if (!ctx.skel) {
163 fprintf(stderr, "failed to open skeleton\n");
164 exit(1);
165 }
166
167 if (use_nop)
168 uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop);
169 else
170 uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop);
171
172 link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe,
173 use_retprobe,
174 -1 /* all PIDs */,
175 "/proc/self/exe",
176 uprobe_offset);
177 if (!link) {
178 fprintf(stderr, "failed to attach uprobe!\n");
179 exit(1);
180 }
181 ctx.skel->links.bench_trigger_uprobe = link;
182 }
183
uprobe_setup_with_nop(void)184 static void uprobe_setup_with_nop(void)
185 {
186 usetup(false, true);
187 }
188
uretprobe_setup_with_nop(void)189 static void uretprobe_setup_with_nop(void)
190 {
191 usetup(true, true);
192 }
193
uprobe_setup_without_nop(void)194 static void uprobe_setup_without_nop(void)
195 {
196 usetup(false, false);
197 }
198
uretprobe_setup_without_nop(void)199 static void uretprobe_setup_without_nop(void)
200 {
201 usetup(true, false);
202 }
203
204 const struct bench bench_trig_base = {
205 .name = "trig-base",
206 .validate = trigger_validate,
207 .producer_thread = trigger_base_producer,
208 .consumer_thread = trigger_consumer,
209 .measure = trigger_base_measure,
210 .report_progress = hits_drops_report_progress,
211 .report_final = hits_drops_report_final,
212 };
213
214 const struct bench bench_trig_tp = {
215 .name = "trig-tp",
216 .validate = trigger_validate,
217 .setup = trigger_tp_setup,
218 .producer_thread = trigger_producer,
219 .consumer_thread = trigger_consumer,
220 .measure = trigger_measure,
221 .report_progress = hits_drops_report_progress,
222 .report_final = hits_drops_report_final,
223 };
224
225 const struct bench bench_trig_rawtp = {
226 .name = "trig-rawtp",
227 .validate = trigger_validate,
228 .setup = trigger_rawtp_setup,
229 .producer_thread = trigger_producer,
230 .consumer_thread = trigger_consumer,
231 .measure = trigger_measure,
232 .report_progress = hits_drops_report_progress,
233 .report_final = hits_drops_report_final,
234 };
235
236 const struct bench bench_trig_kprobe = {
237 .name = "trig-kprobe",
238 .validate = trigger_validate,
239 .setup = trigger_kprobe_setup,
240 .producer_thread = trigger_producer,
241 .consumer_thread = trigger_consumer,
242 .measure = trigger_measure,
243 .report_progress = hits_drops_report_progress,
244 .report_final = hits_drops_report_final,
245 };
246
247 const struct bench bench_trig_fentry = {
248 .name = "trig-fentry",
249 .validate = trigger_validate,
250 .setup = trigger_fentry_setup,
251 .producer_thread = trigger_producer,
252 .consumer_thread = trigger_consumer,
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_fentry_sleep = {
259 .name = "trig-fentry-sleep",
260 .validate = trigger_validate,
261 .setup = trigger_fentry_sleep_setup,
262 .producer_thread = trigger_producer,
263 .consumer_thread = trigger_consumer,
264 .measure = trigger_measure,
265 .report_progress = hits_drops_report_progress,
266 .report_final = hits_drops_report_final,
267 };
268
269 const struct bench bench_trig_fmodret = {
270 .name = "trig-fmodret",
271 .validate = trigger_validate,
272 .setup = trigger_fmodret_setup,
273 .producer_thread = trigger_producer,
274 .consumer_thread = trigger_consumer,
275 .measure = trigger_measure,
276 .report_progress = hits_drops_report_progress,
277 .report_final = hits_drops_report_final,
278 };
279
280 const struct bench bench_trig_uprobe_base = {
281 .name = "trig-uprobe-base",
282 .setup = NULL, /* no uprobe/uretprobe is attached */
283 .producer_thread = uprobe_base_producer,
284 .consumer_thread = trigger_consumer,
285 .measure = trigger_base_measure,
286 .report_progress = hits_drops_report_progress,
287 .report_final = hits_drops_report_final,
288 };
289
290 const struct bench bench_trig_uprobe_with_nop = {
291 .name = "trig-uprobe-with-nop",
292 .setup = uprobe_setup_with_nop,
293 .producer_thread = uprobe_producer_with_nop,
294 .consumer_thread = trigger_consumer,
295 .measure = trigger_measure,
296 .report_progress = hits_drops_report_progress,
297 .report_final = hits_drops_report_final,
298 };
299
300 const struct bench bench_trig_uretprobe_with_nop = {
301 .name = "trig-uretprobe-with-nop",
302 .setup = uretprobe_setup_with_nop,
303 .producer_thread = uprobe_producer_with_nop,
304 .consumer_thread = trigger_consumer,
305 .measure = trigger_measure,
306 .report_progress = hits_drops_report_progress,
307 .report_final = hits_drops_report_final,
308 };
309
310 const struct bench bench_trig_uprobe_without_nop = {
311 .name = "trig-uprobe-without-nop",
312 .setup = uprobe_setup_without_nop,
313 .producer_thread = uprobe_producer_without_nop,
314 .consumer_thread = trigger_consumer,
315 .measure = trigger_measure,
316 .report_progress = hits_drops_report_progress,
317 .report_final = hits_drops_report_final,
318 };
319
320 const struct bench bench_trig_uretprobe_without_nop = {
321 .name = "trig-uretprobe-without-nop",
322 .setup = uretprobe_setup_without_nop,
323 .producer_thread = uprobe_producer_without_nop,
324 .consumer_thread = trigger_consumer,
325 .measure = trigger_measure,
326 .report_progress = hits_drops_report_progress,
327 .report_final = hits_drops_report_final,
328 };
329