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 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 184 static void uprobe_setup_with_nop(void) 185 { 186 usetup(false, true); 187 } 188 189 static void uretprobe_setup_with_nop(void) 190 { 191 usetup(true, true); 192 } 193 194 static void uprobe_setup_without_nop(void) 195 { 196 usetup(false, false); 197 } 198 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