1 // SPDX-License-Identifier: GPL-2.0
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>
22 #define MMAP_FLUSH_DEFAULT 1
24 static int iterations
= 100;
25 static int nr_events
= 1;
26 static const char *event_string
= "dummy";
28 static inline u64
timeval2usec(struct timeval
*tv
)
30 return tv
->tv_sec
* USEC_PER_SEC
+ tv
->tv_usec
;
33 static struct record_opts opts
= {
35 .mmap_pages
= UINT_MAX
,
36 .user_freq
= UINT_MAX
,
37 .user_interval
= ULLONG_MAX
,
41 .default_per_cpu
= true,
43 .mmap_flush
= MMAP_FLUSH_DEFAULT
,
44 .nr_threads_synthesize
= 1,
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"),
63 static const char *const bench_usage
[] = {
64 "perf bench internals evlist-open-close <options>",
68 static int evlist__count_evsel_fds(struct evlist
*evlist
)
73 evlist__for_each_entry(evlist
, evsel
)
74 cnt
+= evsel
->core
.threads
->nr
* perf_cpu_map__nr(evsel
->core
.cpus
);
79 static struct evlist
*bench__create_evlist(char *evstr
)
81 struct parse_events_error err
;
82 struct evlist
*evlist
= evlist__new();
86 pr_err("Not enough memory to create evlist\n");
90 parse_events_error__init(&err
);
91 ret
= parse_events(evlist
, evstr
, &err
);
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");
97 goto out_delete_evlist
;
99 parse_events_error__exit(&err
);
100 ret
= evlist__create_maps(evlist
, &opts
.target
);
102 pr_err("Not enough memory to create thread/cpu maps\n");
103 goto out_delete_evlist
;
106 evlist__config(evlist
, &opts
, NULL
);
111 evlist__delete(evlist
);
115 static int bench__do_evlist_open_close(struct evlist
*evlist
)
117 char sbuf
[STRERR_BUFSIZE
];
118 int err
= evlist__open(evlist
);
121 pr_err("evlist__open: %s\n", str_error_r(errno
, sbuf
, sizeof(sbuf
)));
125 err
= evlist__mmap(evlist
, opts
.mmap_pages
);
127 pr_err("evlist__mmap: %s\n", str_error_r(errno
, sbuf
, sizeof(sbuf
)));
131 evlist__enable(evlist
);
132 evlist__disable(evlist
);
133 evlist__munmap(evlist
);
134 evlist__close(evlist
);
139 static int bench_evlist_open_close__run(char *evstr
)
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
;
152 init_stats(&time_stats
);
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
);
160 evlist__delete(evlist
);
162 for (i
= 0; i
< iterations
; i
++) {
163 pr_debug("Started iteration %d\n", i
);
164 evlist
= bench__create_evlist(evstr
);
168 gettimeofday(&start
, NULL
);
169 err
= bench__do_evlist_open_close(evlist
);
171 evlist__delete(evlist
);
175 gettimeofday(&end
, NULL
);
176 timersub(&end
, &start
, &diff
);
177 runtime_us
= timeval2usec(&diff
);
178 update_stats(&time_stats
, runtime_us
);
180 evlist__delete(evlist
);
181 pr_debug("Iteration %d took:\t%" PRIu64
"us\n", i
, runtime_us
);
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
);
191 static char *bench__repeat_event_string(const char *evstr
, int n
)
193 char sbuf
[STRERR_BUFSIZE
];
195 int i
, str_size
= strlen(evstr
),
196 final_size
= str_size
* n
+ n
,
197 err
= strbuf_init(&buf
, final_size
);
200 pr_err("strbuf_init: %s\n", str_error_r(err
, sbuf
, sizeof(sbuf
)));
204 for (i
= 0; i
< n
; i
++) {
205 err
= strbuf_add(&buf
, evstr
, str_size
);
207 pr_err("strbuf_add: %s\n", str_error_r(err
, sbuf
, sizeof(sbuf
)));
211 err
= strbuf_addch(&buf
, i
== n
-1 ? '\0' : ',');
213 pr_err("strbuf_addch: %s\n", str_error_r(err
, sbuf
, sizeof(sbuf
)));
218 return strbuf_detach(&buf
, NULL
);
221 strbuf_release(&buf
);
226 int bench_evlist_open_close(int argc
, const char **argv
)
228 char *evstr
, errbuf
[BUFSIZ
];
231 argc
= parse_options(argc
, argv
, options
, bench_usage
, 0);
233 usage_with_options(bench_usage
, options
);
237 err
= target__validate(&opts
.target
);
239 target__strerror(&opts
.target
, err
, errbuf
, sizeof(errbuf
));
240 pr_err("%s\n", errbuf
);
244 err
= target__parse_uid(&opts
.target
);
246 target__strerror(&opts
.target
, err
, errbuf
, sizeof(errbuf
));
247 pr_err("%s", errbuf
);
251 /* Enable ignoring missing threads when -u/-p option is defined. */
252 opts
.ignore_missing_thread
= opts
.target
.uid
!= UINT_MAX
|| opts
.target
.pid
;
254 evstr
= bench__repeat_event_string(event_string
, nr_events
);
260 err
= bench_evlist_open_close__run(evstr
);