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