1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include <inttypes.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <limits.h> 7 #include "bench.h" 8 #include "../util/debug.h" 9 #include "../util/stat.h" 10 #include "../util/evlist.h" 11 #include "../util/evsel.h" 12 #include "../util/strbuf.h" 13 #include "../util/record.h" 14 #include "../util/parse-events.h" 15 #include "internal/threadmap.h" 16 #include "internal/cpumap.h" 17 #include <linux/perf_event.h> 18 #include <linux/kernel.h> 19 #include <linux/time64.h> 20 #include <linux/string.h> 21 #include <subcmd/parse-options.h> 22 23 #define MMAP_FLUSH_DEFAULT 1 24 25 static int iterations = 100; 26 static int nr_events = 1; 27 static const char *event_string = "dummy"; 28 29 static inline u64 timeval2usec(struct timeval *tv) 30 { 31 return tv->tv_sec * USEC_PER_SEC + tv->tv_usec; 32 } 33 34 static struct record_opts opts = { 35 .sample_time = true, 36 .mmap_pages = UINT_MAX, 37 .user_freq = UINT_MAX, 38 .user_interval = ULLONG_MAX, 39 .freq = 4000, 40 .target = { 41 .uses_mmap = true, 42 .default_per_cpu = true, 43 }, 44 .mmap_flush = MMAP_FLUSH_DEFAULT, 45 .nr_threads_synthesize = 1, 46 .ctl_fd = -1, 47 .ctl_fd_ack = -1, 48 }; 49 50 static int evlist__count_evsel_fds(struct evlist *evlist) 51 { 52 struct evsel *evsel; 53 int cnt = 0; 54 55 evlist__for_each_entry(evlist, evsel) 56 cnt += evsel->core.threads->nr * perf_cpu_map__nr(evsel->core.cpus); 57 58 return cnt; 59 } 60 61 static struct evlist *bench__create_evlist(char *evstr, const char *uid_str) 62 { 63 struct parse_events_error err; 64 struct evlist *evlist = evlist__new(); 65 int ret; 66 67 if (!evlist) { 68 pr_err("Not enough memory to create evlist\n"); 69 return NULL; 70 } 71 72 parse_events_error__init(&err); 73 ret = parse_events(evlist, evstr, &err); 74 if (ret) { 75 parse_events_error__print(&err, evstr); 76 parse_events_error__exit(&err); 77 pr_err("Run 'perf list' for a list of valid events\n"); 78 ret = 1; 79 goto out_put_evlist; 80 } 81 parse_events_error__exit(&err); 82 if (uid_str) { 83 uid_t uid = parse_uid(uid_str); 84 85 if (uid == UINT_MAX) { 86 pr_err("Invalid User: %s", uid_str); 87 ret = -EINVAL; 88 goto out_put_evlist; 89 } 90 ret = parse_uid_filter(evlist, uid); 91 if (ret) 92 goto out_put_evlist; 93 } 94 ret = evlist__create_maps(evlist, &opts.target); 95 if (ret < 0) { 96 pr_err("Not enough memory to create thread/cpu maps\n"); 97 goto out_put_evlist; 98 } 99 100 evlist__config(evlist, &opts, NULL); 101 102 return evlist; 103 104 out_put_evlist: 105 evlist__put(evlist); 106 return NULL; 107 } 108 109 static int bench__do_evlist_open_close(struct evlist *evlist) 110 { 111 char sbuf[STRERR_BUFSIZE]; 112 int err = evlist__open(evlist); 113 114 if (err < 0) { 115 pr_err("evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); 116 return err; 117 } 118 119 err = evlist__do_mmap(evlist, opts.mmap_pages); 120 if (err < 0) { 121 pr_err("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); 122 return err; 123 } 124 125 evlist__enable(evlist); 126 evlist__disable(evlist); 127 evlist__do_munmap(evlist); 128 evlist__close(evlist); 129 130 return 0; 131 } 132 133 static int bench_evlist_open_close__run(char *evstr, const char *uid_str) 134 { 135 // used to print statistics only 136 struct evlist *evlist = bench__create_evlist(evstr, uid_str); 137 double time_average, time_stddev; 138 struct timeval start, end, diff; 139 struct stats time_stats; 140 u64 runtime_us; 141 int i, err; 142 143 if (!evlist) 144 return -ENOMEM; 145 146 init_stats(&time_stats); 147 148 printf(" Number of cpus:\t%d\n", 149 perf_cpu_map__nr(evlist__core(evlist)->user_requested_cpus)); 150 printf(" Number of threads:\t%d\n", evlist__core(evlist)->threads->nr); 151 printf(" Number of events:\t%d (%d fds)\n", 152 evlist__nr_entries(evlist), evlist__count_evsel_fds(evlist)); 153 printf(" Number of iterations:\t%d\n", iterations); 154 155 evlist__put(evlist); 156 157 for (i = 0; i < iterations; i++) { 158 pr_debug("Started iteration %d\n", i); 159 evlist = bench__create_evlist(evstr, uid_str); 160 if (!evlist) 161 return -ENOMEM; 162 163 gettimeofday(&start, NULL); 164 err = bench__do_evlist_open_close(evlist); 165 if (err) { 166 evlist__put(evlist); 167 return err; 168 } 169 170 gettimeofday(&end, NULL); 171 timersub(&end, &start, &diff); 172 runtime_us = timeval2usec(&diff); 173 update_stats(&time_stats, runtime_us); 174 175 evlist__put(evlist); 176 pr_debug("Iteration %d took:\t%" PRIu64 "us\n", i, runtime_us); 177 } 178 179 time_average = avg_stats(&time_stats); 180 time_stddev = stddev_stats(&time_stats); 181 printf(" Average open-close took: %.3f usec (+- %.3f usec)\n", time_average, time_stddev); 182 183 return 0; 184 } 185 186 static char *bench__repeat_event_string(const char *evstr, int n) 187 { 188 char sbuf[STRERR_BUFSIZE]; 189 struct strbuf buf; 190 int i, str_size = strlen(evstr), 191 final_size = str_size * n + n, 192 err = strbuf_init(&buf, final_size); 193 194 if (err) { 195 pr_err("strbuf_init: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 196 goto out_error; 197 } 198 199 for (i = 0; i < n; i++) { 200 err = strbuf_add(&buf, evstr, str_size); 201 if (err) { 202 pr_err("strbuf_add: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 203 goto out_error; 204 } 205 206 err = strbuf_addch(&buf, i == n-1 ? '\0' : ','); 207 if (err) { 208 pr_err("strbuf_addch: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 209 goto out_error; 210 } 211 } 212 213 return strbuf_detach(&buf, NULL); 214 215 out_error: 216 strbuf_release(&buf); 217 return NULL; 218 } 219 220 221 int bench_evlist_open_close(int argc, const char **argv) 222 { 223 const char *uid_str = NULL; 224 const struct option options[] = { 225 OPT_STRING('e', "event", &event_string, "event", 226 "event selector. use 'perf list' to list available events"), 227 OPT_INTEGER('n', "nr-events", &nr_events, 228 "number of dummy events to create (default 1). If used with -e, it clones those events n times (1 = no change)"), 229 OPT_INTEGER('i', "iterations", &iterations, 230 "Number of iterations used to compute average (default=100)"), 231 OPT_BOOLEAN('a', "all-cpus", &opts.target.system_wide, 232 "system-wide collection from all CPUs"), 233 OPT_STRING('C', "cpu", &opts.target.cpu_list, "cpu", 234 "list of cpus where to open events"), 235 OPT_STRING('p', "pid", &opts.target.pid, "pid", 236 "record events on existing process id"), 237 OPT_STRING('t', "tid", &opts.target.tid, "tid", 238 "record events on existing thread id"), 239 OPT_STRING('u', "uid", &uid_str, "user", "user to profile"), 240 OPT_BOOLEAN(0, "per-thread", &opts.target.per_thread, "use per-thread mmaps"), 241 OPT_END() 242 }; 243 const char *const bench_usage[] = { 244 "perf bench internals evlist-open-close <options>", 245 NULL 246 }; 247 char *evstr, errbuf[BUFSIZ]; 248 int err; 249 250 argc = parse_options(argc, argv, options, bench_usage, 0); 251 if (argc) { 252 usage_with_options(bench_usage, options); 253 exit(EXIT_FAILURE); 254 } 255 256 err = target__validate(&opts.target); 257 if (err) { 258 target__strerror(&opts.target, err, errbuf, sizeof(errbuf)); 259 pr_err("%s\n", errbuf); 260 goto out; 261 } 262 263 /* Enable ignoring missing threads when -p option is defined. */ 264 opts.ignore_missing_thread = opts.target.pid; 265 266 evstr = bench__repeat_event_string(event_string, nr_events); 267 if (!evstr) { 268 err = -ENOMEM; 269 goto out; 270 } 271 272 err = bench_evlist_open_close__run(evstr, uid_str); 273 274 free(evstr); 275 out: 276 return err; 277 } 278