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