File Index
Files and directories
Sign in
FILE

evlist-open-close.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/bench/evlist-open-close.c C
  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
 25static int iterations = 100;
 26static int nr_events = 1;
 27static const char *event_string = "dummy";
 28
 29static inline u64 timeval2usec(struct timeval *tv)
 30{
 31	return tv->tv_sec * USEC_PER_SEC + tv->tv_usec;
 32}
 33
 34static 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
 50static 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
 61static 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
104out_delete_evlist:
105	evlist__delete(evlist);
106	return NULL;
107}
108
109static 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
133static 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
185static 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
214out_error:
215	strbuf_release(&buf);
216	return NULL;
217}
218
219
220int 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);
274out:
275	return err;
276}
~/ Read-only file preview.