1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* 3 * uprobe.c 4 * 5 * uprobe benchmarks 6 * 7 * Copyright (C) 2023, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> 8 */ 9 #include "../perf.h" 10 #include "../util/util.h" 11 #include <subcmd/parse-options.h> 12 #include "../builtin.h" 13 #include "bench.h" 14 #include <linux/compiler.h> 15 #include <linux/time64.h> 16 17 #include <inttypes.h> 18 #include <stdio.h> 19 #include <sys/time.h> 20 #include <sys/types.h> 21 #include <time.h> 22 #include <unistd.h> 23 #include <stdlib.h> 24 25 #define LOOPS_DEFAULT 1000 26 static int loops = LOOPS_DEFAULT; 27 28 enum bench_uprobe { 29 BENCH_UPROBE__BASELINE, 30 BENCH_UPROBE__EMPTY, 31 BENCH_UPROBE__TRACE_PRINTK, 32 }; 33 34 static const struct option options[] = { 35 OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), 36 OPT_END() 37 }; 38 39 static const char * const bench_uprobe_usage[] = { 40 "perf bench uprobe <options>", 41 NULL 42 }; 43 44 #ifdef HAVE_BPF_SKEL 45 #include "bpf_skel/bench_uprobe.skel.h" 46 47 #define bench_uprobe__attach_uprobe(prog) \ 48 skel->links.prog = bpf_program__attach_uprobe_opts(/*prog=*/skel->progs.prog, \ 49 /*pid=*/-1, \ 50 /*binary_path=*/"/lib64/libc.so.6", \ 51 /*func_offset=*/0, \ 52 /*opts=*/&uprobe_opts); \ 53 if (!skel->links.prog) { \ 54 err = -errno; \ 55 fprintf(stderr, "Failed to attach bench uprobe \"%s\": %s\n", #prog, strerror(errno)); \ 56 goto cleanup; \ 57 } 58 59 struct bench_uprobe_bpf *skel; 60 61 static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench) 62 { 63 DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 64 int err; 65 66 /* Load and verify BPF application */ 67 skel = bench_uprobe_bpf__open(); 68 if (!skel) { 69 fprintf(stderr, "Failed to open and load uprobes bench BPF skeleton\n"); 70 return -1; 71 } 72 73 err = bench_uprobe_bpf__load(skel); 74 if (err) { 75 fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 76 goto cleanup; 77 } 78 79 uprobe_opts.func_name = "usleep"; 80 switch (bench) { 81 case BENCH_UPROBE__BASELINE: break; 82 case BENCH_UPROBE__EMPTY: bench_uprobe__attach_uprobe(empty); break; 83 case BENCH_UPROBE__TRACE_PRINTK: bench_uprobe__attach_uprobe(trace_printk); break; 84 default: 85 fprintf(stderr, "Invalid bench: %d\n", bench); 86 goto cleanup; 87 } 88 89 return err; 90 cleanup: 91 bench_uprobe_bpf__destroy(skel); 92 skel = NULL; 93 return err; 94 } 95 96 static void bench_uprobe__teardown_bpf_skel(void) 97 { 98 if (skel) { 99 bench_uprobe_bpf__destroy(skel); 100 skel = NULL; 101 } 102 } 103 #else 104 static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench __maybe_unused) { return 0; } 105 static void bench_uprobe__teardown_bpf_skel(void) {}; 106 #endif 107 108 static int bench_uprobe_format__default_fprintf(const char *name, const char *unit, u64 diff, FILE *fp) 109 { 110 static u64 baseline, previous; 111 s64 diff_to_baseline = diff - baseline, 112 diff_to_previous = diff - previous; 113 int printed = fprintf(fp, "# Executed %'d %s calls\n", loops, name); 114 115 printed += fprintf(fp, " %14s: %'" PRIu64 " %ss", "Total time", diff, unit); 116 117 if (baseline) { 118 printed += fprintf(fp, " %s%'" PRId64 " to baseline", diff_to_baseline > 0 ? "+" : "", diff_to_baseline); 119 120 if (previous != baseline) 121 fprintf(stdout, " %s%'" PRId64 " to previous", diff_to_previous > 0 ? "+" : "", diff_to_previous); 122 } 123 124 printed += fprintf(fp, "\n\n %'.3f %ss/op", (double)diff / (double)loops, unit); 125 126 if (baseline) { 127 printed += fprintf(fp, " %'.3f %ss/op to baseline", (double)diff_to_baseline / (double)loops, unit); 128 129 if (previous != baseline) 130 printed += fprintf(fp, " %'.3f %ss/op to previous", (double)diff_to_previous / (double)loops, unit); 131 } else { 132 baseline = diff; 133 } 134 135 fputc('\n', fp); 136 137 previous = diff; 138 139 return printed + 1; 140 } 141 142 static int bench_uprobe(int argc, const char **argv, enum bench_uprobe bench) 143 { 144 const char *name = "usleep(1000)", *unit = "usec"; 145 struct timespec start, end; 146 u64 diff; 147 int i; 148 149 argc = parse_options(argc, argv, options, bench_uprobe_usage, 0); 150 151 if (bench != BENCH_UPROBE__BASELINE && bench_uprobe__setup_bpf_skel(bench) < 0) 152 return 0; 153 154 clock_gettime(CLOCK_REALTIME, &start); 155 156 for (i = 0; i < loops; i++) { 157 usleep(USEC_PER_MSEC); 158 } 159 160 clock_gettime(CLOCK_REALTIME, &end); 161 162 diff = end.tv_sec * NSEC_PER_SEC + end.tv_nsec - (start.tv_sec * NSEC_PER_SEC + start.tv_nsec); 163 diff /= NSEC_PER_USEC; 164 165 switch (bench_format) { 166 case BENCH_FORMAT_DEFAULT: 167 bench_uprobe_format__default_fprintf(name, unit, diff, stdout); 168 break; 169 170 case BENCH_FORMAT_SIMPLE: 171 printf("%" PRIu64 "\n", diff); 172 break; 173 174 default: 175 /* reaching here is something of a disaster */ 176 fprintf(stderr, "Unknown format:%d\n", bench_format); 177 exit(1); 178 } 179 180 if (bench != BENCH_UPROBE__BASELINE) 181 bench_uprobe__teardown_bpf_skel(); 182 183 return 0; 184 } 185 186 int bench_uprobe_baseline(int argc, const char **argv) 187 { 188 return bench_uprobe(argc, argv, BENCH_UPROBE__BASELINE); 189 } 190 191 int bench_uprobe_empty(int argc, const char **argv) 192 { 193 return bench_uprobe(argc, argv, BENCH_UPROBE__EMPTY); 194 } 195 196 int bench_uprobe_trace_printk(int argc, const char **argv) 197 { 198 return bench_uprobe(argc, argv, BENCH_UPROBE__TRACE_PRINTK); 199 } 200