1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2020 Facebook */ 3 #define _GNU_SOURCE 4 #include <unistd.h> 5 #include "bench.h" 6 #include "trigger_bench.skel.h" 7 #include "trace_helpers.h" 8 9 /* adjust slot shift in inc_hits() if changing */ 10 #define MAX_BUCKETS 256 11 12 #pragma GCC diagnostic ignored "-Wattributes" 13 14 /* BPF triggering benchmarks */ 15 static struct trigger_ctx { 16 struct trigger_bench *skel; 17 } ctx; 18 19 static struct counter base_hits[MAX_BUCKETS]; 20 21 static __always_inline void inc_counter(struct counter *counters) 22 { 23 static __thread int tid = 0; 24 unsigned slot; 25 26 if (unlikely(tid == 0)) 27 tid = syscall(SYS_gettid); 28 29 /* multiplicative hashing, it's fast */ 30 slot = 2654435769U * tid; 31 slot >>= 24; 32 33 atomic_inc(&base_hits[slot].value); /* use highest byte as an index */ 34 } 35 36 static long sum_and_reset_counters(struct counter *counters) 37 { 38 int i; 39 long sum = 0; 40 41 for (i = 0; i < MAX_BUCKETS; i++) 42 sum += atomic_swap(&counters[i].value, 0); 43 return sum; 44 } 45 46 static void trigger_validate(void) 47 { 48 if (env.consumer_cnt != 0) { 49 fprintf(stderr, "benchmark doesn't support consumer!\n"); 50 exit(1); 51 } 52 } 53 54 static void *trigger_base_producer(void *input) 55 { 56 while (true) { 57 (void)syscall(__NR_getpgid); 58 inc_counter(base_hits); 59 } 60 return NULL; 61 } 62 63 static void trigger_base_measure(struct bench_res *res) 64 { 65 res->hits = sum_and_reset_counters(base_hits); 66 } 67 68 static void *trigger_producer(void *input) 69 { 70 while (true) 71 (void)syscall(__NR_getpgid); 72 return NULL; 73 } 74 75 static void trigger_measure(struct bench_res *res) 76 { 77 res->hits = sum_and_reset_counters(ctx.skel->bss->hits); 78 } 79 80 static void setup_ctx(void) 81 { 82 setup_libbpf(); 83 84 ctx.skel = trigger_bench__open_and_load(); 85 if (!ctx.skel) { 86 fprintf(stderr, "failed to open skeleton\n"); 87 exit(1); 88 } 89 } 90 91 static void attach_bpf(struct bpf_program *prog) 92 { 93 struct bpf_link *link; 94 95 link = bpf_program__attach(prog); 96 if (!link) { 97 fprintf(stderr, "failed to attach program!\n"); 98 exit(1); 99 } 100 } 101 102 static void trigger_tp_setup(void) 103 { 104 setup_ctx(); 105 attach_bpf(ctx.skel->progs.bench_trigger_tp); 106 } 107 108 static void trigger_rawtp_setup(void) 109 { 110 setup_ctx(); 111 attach_bpf(ctx.skel->progs.bench_trigger_raw_tp); 112 } 113 114 static void trigger_kprobe_setup(void) 115 { 116 setup_ctx(); 117 attach_bpf(ctx.skel->progs.bench_trigger_kprobe); 118 } 119 120 static void trigger_kretprobe_setup(void) 121 { 122 setup_ctx(); 123 attach_bpf(ctx.skel->progs.bench_trigger_kretprobe); 124 } 125 126 static void trigger_kprobe_multi_setup(void) 127 { 128 setup_ctx(); 129 attach_bpf(ctx.skel->progs.bench_trigger_kprobe_multi); 130 } 131 132 static void trigger_kretprobe_multi_setup(void) 133 { 134 setup_ctx(); 135 attach_bpf(ctx.skel->progs.bench_trigger_kretprobe_multi); 136 } 137 138 static void trigger_fentry_setup(void) 139 { 140 setup_ctx(); 141 attach_bpf(ctx.skel->progs.bench_trigger_fentry); 142 } 143 144 static void trigger_fexit_setup(void) 145 { 146 setup_ctx(); 147 attach_bpf(ctx.skel->progs.bench_trigger_fexit); 148 } 149 150 static void trigger_fentry_sleep_setup(void) 151 { 152 setup_ctx(); 153 attach_bpf(ctx.skel->progs.bench_trigger_fentry_sleep); 154 } 155 156 static void trigger_fmodret_setup(void) 157 { 158 setup_ctx(); 159 attach_bpf(ctx.skel->progs.bench_trigger_fmodret); 160 } 161 162 /* make sure call is not inlined and not avoided by compiler, so __weak and 163 * inline asm volatile in the body of the function 164 * 165 * There is a performance difference between uprobing at nop location vs other 166 * instructions. So use two different targets, one of which starts with nop 167 * and another doesn't. 168 * 169 * GCC doesn't generate stack setup preample for these functions due to them 170 * having no input arguments and doing nothing in the body. 171 */ 172 __nocf_check __weak void uprobe_target_nop(void) 173 { 174 asm volatile ("nop"); 175 } 176 177 __weak void opaque_noop_func(void) 178 { 179 } 180 181 __nocf_check __weak int uprobe_target_push(void) 182 { 183 /* overhead of function call is negligible compared to uprobe 184 * triggering, so this shouldn't affect benchmark results much 185 */ 186 opaque_noop_func(); 187 return 1; 188 } 189 190 __nocf_check __weak void uprobe_target_ret(void) 191 { 192 asm volatile (""); 193 } 194 195 static void *uprobe_base_producer(void *input) 196 { 197 while (true) { 198 uprobe_target_nop(); 199 inc_counter(base_hits); 200 } 201 return NULL; 202 } 203 204 static void *uprobe_producer_nop(void *input) 205 { 206 while (true) 207 uprobe_target_nop(); 208 return NULL; 209 } 210 211 static void *uprobe_producer_push(void *input) 212 { 213 while (true) 214 uprobe_target_push(); 215 return NULL; 216 } 217 218 static void *uprobe_producer_ret(void *input) 219 { 220 while (true) 221 uprobe_target_ret(); 222 return NULL; 223 } 224 225 static void usetup(bool use_retprobe, void *target_addr) 226 { 227 size_t uprobe_offset; 228 struct bpf_link *link; 229 230 setup_libbpf(); 231 232 ctx.skel = trigger_bench__open_and_load(); 233 if (!ctx.skel) { 234 fprintf(stderr, "failed to open skeleton\n"); 235 exit(1); 236 } 237 238 uprobe_offset = get_uprobe_offset(target_addr); 239 link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe, 240 use_retprobe, 241 -1 /* all PIDs */, 242 "/proc/self/exe", 243 uprobe_offset); 244 if (!link) { 245 fprintf(stderr, "failed to attach uprobe!\n"); 246 exit(1); 247 } 248 ctx.skel->links.bench_trigger_uprobe = link; 249 } 250 251 static void uprobe_setup_nop(void) 252 { 253 usetup(false, &uprobe_target_nop); 254 } 255 256 static void uretprobe_setup_nop(void) 257 { 258 usetup(true, &uprobe_target_nop); 259 } 260 261 static void uprobe_setup_push(void) 262 { 263 usetup(false, &uprobe_target_push); 264 } 265 266 static void uretprobe_setup_push(void) 267 { 268 usetup(true, &uprobe_target_push); 269 } 270 271 static void uprobe_setup_ret(void) 272 { 273 usetup(false, &uprobe_target_ret); 274 } 275 276 static void uretprobe_setup_ret(void) 277 { 278 usetup(true, &uprobe_target_ret); 279 } 280 281 const struct bench bench_trig_base = { 282 .name = "trig-base", 283 .validate = trigger_validate, 284 .producer_thread = trigger_base_producer, 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_tp = { 291 .name = "trig-tp", 292 .validate = trigger_validate, 293 .setup = trigger_tp_setup, 294 .producer_thread = trigger_producer, 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_rawtp = { 301 .name = "trig-rawtp", 302 .validate = trigger_validate, 303 .setup = trigger_rawtp_setup, 304 .producer_thread = trigger_producer, 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_kprobe = { 311 .name = "trig-kprobe", 312 .validate = trigger_validate, 313 .setup = trigger_kprobe_setup, 314 .producer_thread = trigger_producer, 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_kretprobe = { 321 .name = "trig-kretprobe", 322 .validate = trigger_validate, 323 .setup = trigger_kretprobe_setup, 324 .producer_thread = trigger_producer, 325 .measure = trigger_measure, 326 .report_progress = hits_drops_report_progress, 327 .report_final = hits_drops_report_final, 328 }; 329 330 const struct bench bench_trig_kprobe_multi = { 331 .name = "trig-kprobe-multi", 332 .validate = trigger_validate, 333 .setup = trigger_kprobe_multi_setup, 334 .producer_thread = trigger_producer, 335 .measure = trigger_measure, 336 .report_progress = hits_drops_report_progress, 337 .report_final = hits_drops_report_final, 338 }; 339 340 const struct bench bench_trig_kretprobe_multi = { 341 .name = "trig-kretprobe-multi", 342 .validate = trigger_validate, 343 .setup = trigger_kretprobe_multi_setup, 344 .producer_thread = trigger_producer, 345 .measure = trigger_measure, 346 .report_progress = hits_drops_report_progress, 347 .report_final = hits_drops_report_final, 348 }; 349 350 const struct bench bench_trig_fentry = { 351 .name = "trig-fentry", 352 .validate = trigger_validate, 353 .setup = trigger_fentry_setup, 354 .producer_thread = trigger_producer, 355 .measure = trigger_measure, 356 .report_progress = hits_drops_report_progress, 357 .report_final = hits_drops_report_final, 358 }; 359 360 const struct bench bench_trig_fexit = { 361 .name = "trig-fexit", 362 .validate = trigger_validate, 363 .setup = trigger_fexit_setup, 364 .producer_thread = trigger_producer, 365 .measure = trigger_measure, 366 .report_progress = hits_drops_report_progress, 367 .report_final = hits_drops_report_final, 368 }; 369 370 const struct bench bench_trig_fentry_sleep = { 371 .name = "trig-fentry-sleep", 372 .validate = trigger_validate, 373 .setup = trigger_fentry_sleep_setup, 374 .producer_thread = trigger_producer, 375 .measure = trigger_measure, 376 .report_progress = hits_drops_report_progress, 377 .report_final = hits_drops_report_final, 378 }; 379 380 const struct bench bench_trig_fmodret = { 381 .name = "trig-fmodret", 382 .validate = trigger_validate, 383 .setup = trigger_fmodret_setup, 384 .producer_thread = trigger_producer, 385 .measure = trigger_measure, 386 .report_progress = hits_drops_report_progress, 387 .report_final = hits_drops_report_final, 388 }; 389 390 const struct bench bench_trig_uprobe_base = { 391 .name = "trig-uprobe-base", 392 .setup = NULL, /* no uprobe/uretprobe is attached */ 393 .producer_thread = uprobe_base_producer, 394 .measure = trigger_base_measure, 395 .report_progress = hits_drops_report_progress, 396 .report_final = hits_drops_report_final, 397 }; 398 399 const struct bench bench_trig_uprobe_nop = { 400 .name = "trig-uprobe-nop", 401 .setup = uprobe_setup_nop, 402 .producer_thread = uprobe_producer_nop, 403 .measure = trigger_measure, 404 .report_progress = hits_drops_report_progress, 405 .report_final = hits_drops_report_final, 406 }; 407 408 const struct bench bench_trig_uretprobe_nop = { 409 .name = "trig-uretprobe-nop", 410 .setup = uretprobe_setup_nop, 411 .producer_thread = uprobe_producer_nop, 412 .measure = trigger_measure, 413 .report_progress = hits_drops_report_progress, 414 .report_final = hits_drops_report_final, 415 }; 416 417 const struct bench bench_trig_uprobe_push = { 418 .name = "trig-uprobe-push", 419 .setup = uprobe_setup_push, 420 .producer_thread = uprobe_producer_push, 421 .measure = trigger_measure, 422 .report_progress = hits_drops_report_progress, 423 .report_final = hits_drops_report_final, 424 }; 425 426 const struct bench bench_trig_uretprobe_push = { 427 .name = "trig-uretprobe-push", 428 .setup = uretprobe_setup_push, 429 .producer_thread = uprobe_producer_push, 430 .measure = trigger_measure, 431 .report_progress = hits_drops_report_progress, 432 .report_final = hits_drops_report_final, 433 }; 434 435 const struct bench bench_trig_uprobe_ret = { 436 .name = "trig-uprobe-ret", 437 .setup = uprobe_setup_ret, 438 .producer_thread = uprobe_producer_ret, 439 .measure = trigger_measure, 440 .report_progress = hits_drops_report_progress, 441 .report_final = hits_drops_report_final, 442 }; 443 444 const struct bench bench_trig_uretprobe_ret = { 445 .name = "trig-uretprobe-ret", 446 .setup = uretprobe_setup_ret, 447 .producer_thread = uprobe_producer_ret, 448 .measure = trigger_measure, 449 .report_progress = hits_drops_report_progress, 450 .report_final = hits_drops_report_final, 451 }; 452