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 */ 8static struct trigger_ctx { 9 struct trigger_bench *skel; 10} ctx; 11 12static struct counter base_hits; 13 14static 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 22static 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 31static void trigger_base_measure(struct bench_res *res) 32{ 33 res->hits = atomic_swap(&base_hits.value, 0); 34} 35 36static void *trigger_producer(void *input) 37{ 38 while (true) 39 (void)syscall(__NR_getpgid); 40 return NULL; 41} 42 43static void trigger_measure(struct bench_res *res) 44{ 45 res->hits = atomic_swap(&ctx.skel->bss->hits, 0); 46} 47 48static 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 59static 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 70static void trigger_tp_setup(void) 71{ 72 setup_ctx(); 73 attach_bpf(ctx.skel->progs.bench_trigger_tp); 74} 75 76static void trigger_rawtp_setup(void) 77{ 78 setup_ctx(); 79 attach_bpf(ctx.skel->progs.bench_trigger_raw_tp); 80} 81 82static void trigger_kprobe_setup(void) 83{ 84 setup_ctx(); 85 attach_bpf(ctx.skel->progs.bench_trigger_kprobe); 86} 87 88static void trigger_fentry_setup(void) 89{ 90 setup_ctx(); 91 attach_bpf(ctx.skel->progs.bench_trigger_fentry); 92} 93 94static void trigger_fentry_sleep_setup(void) 95{ 96 setup_ctx(); 97 attach_bpf(ctx.skel->progs.bench_trigger_fentry_sleep); 98} 99 100static 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 */ 116__weak void uprobe_target_with_nop(void) 117{ 118 asm volatile ("nop"); 119} 120 121__weak void uprobe_target_without_nop(void) 122{ 123 asm volatile (""); 124} 125 126static 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 135static void *uprobe_producer_with_nop(void *input) 136{ 137 while (true) 138 uprobe_target_with_nop(); 139 return NULL; 140} 141 142static void *uprobe_producer_without_nop(void *input) 143{ 144 while (true) 145 uprobe_target_without_nop(); 146 return NULL; 147} 148 149static 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 179static void uprobe_setup_with_nop(void) 180{ 181 usetup(false, true); 182} 183 184static void uretprobe_setup_with_nop(void) 185{ 186 usetup(true, true); 187} 188 189static void uprobe_setup_without_nop(void) 190{ 191 usetup(false, false); 192} 193 194static void uretprobe_setup_without_nop(void) 195{ 196 usetup(true, false); 197} 198 199const 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 208const 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 218const 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 228const 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 238const 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 248const 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 258const 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 268const 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 277const 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 286const 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 295const 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 304const 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