266 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 123 24static int iterations = 100;25static int nr_events = 1;26static const char *event_string = "dummy";27 28static inline u64 timeval2usec(struct timeval *tv)29{30 return tv->tv_sec * USEC_PER_SEC + tv->tv_usec;31}32 33static 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 49static 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 63static const char *const bench_usage[] = {64 "perf bench internals evlist-open-close <options>",65 NULL66};67 68static 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 * perf_cpu_map__nr(evsel->core.cpus);75 76 return cnt;77}78 79static 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 110out_delete_evlist:111 evlist__delete(evlist);112 return NULL;113}114 115static 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 139static int bench_evlist_open_close__run(char *evstr)140{141 // used to print statistics only142 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", perf_cpu_map__nr(evlist->core.user_requested_cpus));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 191static 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 220out_error:221 strbuf_release(&buf);222 return NULL;223}224 225 226int 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);263out:264 return err;265}266