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=*/"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 return err; 93 } 94 95 static void bench_uprobe__teardown_bpf_skel(void) 96 { 97 if (skel) { 98 bench_uprobe_bpf__destroy(skel); 99 skel = NULL; 100 } 101 } 102 #else 103 static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench __maybe_unused) { return 0; } 104 static void bench_uprobe__teardown_bpf_skel(void) {}; 105 #endif 106 107 static int bench_uprobe_format__default_fprintf(const char *name, const char *unit, u64 diff, FILE *fp) 108 { 109 static u64 baseline, previous; 110 s64 diff_to_baseline = diff - baseline, 111 diff_to_previous = diff - previous; 112 int printed = fprintf(fp, "# Executed %'d %s calls\n", loops, name); 113 114 printed += fprintf(fp, " %14s: %'" PRIu64 " %ss", "Total time", diff, unit); 115 116 if (baseline) { 117 printed += fprintf(fp, " %s%'" PRId64 " to baseline", diff_to_baseline > 0 ? "+" : "", diff_to_baseline); 118 119 if (previous != baseline) 120 fprintf(stdout, " %s%'" PRId64 " to previous", diff_to_previous > 0 ? "+" : "", diff_to_previous); 121 } 122 123 printed += fprintf(fp, "\n\n %'.3f %ss/op", (double)diff / (double)loops, unit); 124 125 if (baseline) { 126 printed += fprintf(fp, " %'.3f %ss/op to baseline", (double)diff_to_baseline / (double)loops, unit); 127 128 if (previous != baseline) 129 printed += fprintf(fp, " %'.3f %ss/op to previous", (double)diff_to_previous / (double)loops, unit); 130 } else { 131 baseline = diff; 132 } 133 134 fputc('\n', fp); 135 136 previous = diff; 137 138 return printed + 1; 139 } 140 141 static int bench_uprobe(int argc, const char **argv, enum bench_uprobe bench) 142 { 143 const char *name = "usleep(1000)", *unit = "usec"; 144 struct timespec start, end; 145 u64 diff; 146 int i; 147 148 argc = parse_options(argc, argv, options, bench_uprobe_usage, 0); 149 150 if (bench != BENCH_UPROBE__BASELINE && bench_uprobe__setup_bpf_skel(bench) < 0) 151 return 0; 152 153 clock_gettime(CLOCK_REALTIME, &start); 154 155 for (i = 0; i < loops; i++) { 156 usleep(USEC_PER_MSEC); 157 } 158 159 clock_gettime(CLOCK_REALTIME, &end); 160 161 diff = end.tv_sec * NSEC_PER_SEC + end.tv_nsec - (start.tv_sec * NSEC_PER_SEC + start.tv_nsec); 162 diff /= NSEC_PER_USEC; 163 164 switch (bench_format) { 165 case BENCH_FORMAT_DEFAULT: 166 bench_uprobe_format__default_fprintf(name, unit, diff, stdout); 167 break; 168 169 case BENCH_FORMAT_SIMPLE: 170 printf("%" PRIu64 "\n", diff); 171 break; 172 173 default: 174 /* reaching here is something of a disaster */ 175 fprintf(stderr, "Unknown format:%d\n", bench_format); 176 exit(1); 177 } 178 179 if (bench != BENCH_UPROBE__BASELINE) 180 bench_uprobe__teardown_bpf_skel(); 181 182 return 0; 183 } 184 185 int bench_uprobe_baseline(int argc, const char **argv) 186 { 187 return bench_uprobe(argc, argv, BENCH_UPROBE__BASELINE); 188 } 189 190 int bench_uprobe_empty(int argc, const char **argv) 191 { 192 return bench_uprobe(argc, argv, BENCH_UPROBE__EMPTY); 193 } 194 195 int bench_uprobe_trace_printk(int argc, const char **argv) 196 { 197 return bench_uprobe(argc, argv, BENCH_UPROBE__TRACE_PRINTK); 198 } 199