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 * perf_cpu_map__nr(evsel->core.cpus); |
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: &err); |
91 | ret = parse_events(evlist, str: evstr, err: &err); |
92 | if (ret) { |
93 | parse_events_error__print(err: &err, event: evstr); |
94 | parse_events_error__exit(err: &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: &err); |
100 | ret = evlist__create_maps(evlist, target: &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: &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, pages: 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(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(tv: &diff); |
178 | update_stats(stats: &time_stats, val: 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(stats: &time_stats); |
185 | time_stddev = stddev_stats(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: &buf, hint: 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: &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(sb: &buf, c: 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: &buf, NULL); |
219 | |
220 | out_error: |
221 | strbuf_release(buf: &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(evstr: event_string, n: 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 | |