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_delete_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_delete_evlist; 89 } 90 ret = parse_uid_filter(evlist, uid); 91 if (ret) 92 goto out_delete_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_delete_evlist; 98 } 99 100 evlist__config(evlist, &opts, NULL); 101 102 return evlist; 103 104 out_delete_evlist: 105 evlist__delete(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__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__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", perf_cpu_map__nr(evlist->core.user_requested_cpus)); 149 printf(" Number of threads:\t%d\n", evlist->core.threads->nr); 150 printf(" Number of events:\t%d (%d fds)\n", 151 evlist->core.nr_entries, evlist__count_evsel_fds(evlist)); 152 printf(" Number of iterations:\t%d\n", iterations); 153 154 evlist__delete(evlist); 155 156 for (i = 0; i < iterations; i++) { 157 pr_debug("Started iteration %d\n", i); 158 evlist = bench__create_evlist(evstr, uid_str); 159 if (!evlist) 160 return -ENOMEM; 161 162 gettimeofday(&start, NULL); 163 err = bench__do_evlist_open_close(evlist); 164 if (err) { 165 evlist__delete(evlist); 166 return err; 167 } 168 169 gettimeofday(&end, NULL); 170 timersub(&end, &start, &diff); 171 runtime_us = timeval2usec(&diff); 172 update_stats(&time_stats, runtime_us); 173 174 evlist__delete(evlist); 175 pr_debug("Iteration %d took:\t%" PRIu64 "us\n", i, runtime_us); 176 } 177 178 time_average = avg_stats(&time_stats); 179 time_stddev = stddev_stats(&time_stats); 180 printf(" Average open-close took: %.3f usec (+- %.3f usec)\n", time_average, time_stddev); 181 182 return 0; 183 } 184 185 static char *bench__repeat_event_string(const char *evstr, int n) 186 { 187 char sbuf[STRERR_BUFSIZE]; 188 struct strbuf buf; 189 int i, str_size = strlen(evstr), 190 final_size = str_size * n + n, 191 err = strbuf_init(&buf, final_size); 192 193 if (err) { 194 pr_err("strbuf_init: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 195 goto out_error; 196 } 197 198 for (i = 0; i < n; i++) { 199 err = strbuf_add(&buf, evstr, str_size); 200 if (err) { 201 pr_err("strbuf_add: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 202 goto out_error; 203 } 204 205 err = strbuf_addch(&buf, i == n-1 ? '\0' : ','); 206 if (err) { 207 pr_err("strbuf_addch: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); 208 goto out_error; 209 } 210 } 211 212 return strbuf_detach(&buf, NULL); 213 214 out_error: 215 strbuf_release(&buf); 216 return NULL; 217 } 218 219 220 int bench_evlist_open_close(int argc, const char **argv) 221 { 222 const char *uid_str = NULL; 223 const struct option options[] = { 224 OPT_STRING('e', "event", &event_string, "event", 225 "event selector. use 'perf list' to list available events"), 226 OPT_INTEGER('n', "nr-events", &nr_events, 227 "number of dummy events to create (default 1). If used with -e, it clones those events n times (1 = no change)"), 228 OPT_INTEGER('i', "iterations", &iterations, 229 "Number of iterations used to compute average (default=100)"), 230 OPT_BOOLEAN('a', "all-cpus", &opts.target.system_wide, 231 "system-wide collection from all CPUs"), 232 OPT_STRING('C', "cpu", &opts.target.cpu_list, "cpu", 233 "list of cpus where to open events"), 234 OPT_STRING('p', "pid", &opts.target.pid, "pid", 235 "record events on existing process id"), 236 OPT_STRING('t', "tid", &opts.target.tid, "tid", 237 "record events on existing thread id"), 238 OPT_STRING('u', "uid", &uid_str, "user", "user to profile"), 239 OPT_BOOLEAN(0, "per-thread", &opts.target.per_thread, "use per-thread mmaps"), 240 OPT_END() 241 }; 242 const char *const bench_usage[] = { 243 "perf bench internals evlist-open-close <options>", 244 NULL 245 }; 246 char *evstr, errbuf[BUFSIZ]; 247 int err; 248 249 argc = parse_options(argc, argv, options, bench_usage, 0); 250 if (argc) { 251 usage_with_options(bench_usage, options); 252 exit(EXIT_FAILURE); 253 } 254 255 err = target__validate(&opts.target); 256 if (err) { 257 target__strerror(&opts.target, err, errbuf, sizeof(errbuf)); 258 pr_err("%s\n", errbuf); 259 goto out; 260 } 261 262 /* Enable ignoring missing threads when -p option is defined. */ 263 opts.ignore_missing_thread = opts.target.pid; 264 265 evstr = bench__repeat_event_string(event_string, nr_events); 266 if (!evstr) { 267 err = -ENOMEM; 268 goto out; 269 } 270 271 err = bench_evlist_open_close__run(evstr, uid_str); 272 273 free(evstr); 274 out: 275 return err; 276 } 277