1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * builtin-bench.c
4 *
5 * General benchmarking collections provided by perf
6 *
7 * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
8 */
9
10 /*
11 * Available benchmark collection list:
12 *
13 * sched ... scheduler and IPC performance
14 * syscall ... System call performance
15 * mem ... memory access performance
16 * numa ... NUMA scheduling and MM performance
17 * futex ... Futex performance
18 * epoll ... Event poll performance
19 */
20 #include <subcmd/parse-options.h>
21 #include "builtin.h"
22 #include "bench/bench.h"
23
24 #include <locale.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/prctl.h>
29 #include <linux/zalloc.h>
30
31 typedef int (*bench_fn_t)(int argc, const char **argv);
32
33 struct bench {
34 const char *name;
35 const char *summary;
36 bench_fn_t fn;
37 };
38
39 #ifdef HAVE_LIBNUMA_SUPPORT
40 static struct bench numa_benchmarks[] = {
41 { "mem", "Benchmark for NUMA workloads", bench_numa },
42 { "all", "Run all NUMA benchmarks", NULL },
43 { NULL, NULL, NULL }
44 };
45 #endif
46
47 static struct bench sched_benchmarks[] = {
48 { "messaging", "Benchmark for scheduling and IPC", bench_sched_messaging },
49 { "pipe", "Benchmark for pipe() between two processes", bench_sched_pipe },
50 { "all", "Run all scheduler benchmarks", NULL },
51 { NULL, NULL, NULL }
52 };
53
54 static struct bench syscall_benchmarks[] = {
55 { "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic },
56 { "all", "Run all syscall benchmarks", NULL },
57 { NULL, NULL, NULL },
58 };
59
60 static struct bench mem_benchmarks[] = {
61 { "memcpy", "Benchmark for memcpy() functions", bench_mem_memcpy },
62 { "memset", "Benchmark for memset() functions", bench_mem_memset },
63 { "find_bit", "Benchmark for find_bit() functions", bench_mem_find_bit },
64 { "all", "Run all memory access benchmarks", NULL },
65 { NULL, NULL, NULL }
66 };
67
68 static struct bench futex_benchmarks[] = {
69 { "hash", "Benchmark for futex hash table", bench_futex_hash },
70 { "wake", "Benchmark for futex wake calls", bench_futex_wake },
71 { "wake-parallel", "Benchmark for parallel futex wake calls", bench_futex_wake_parallel },
72 { "requeue", "Benchmark for futex requeue calls", bench_futex_requeue },
73 /* pi-futexes */
74 { "lock-pi", "Benchmark for futex lock_pi calls", bench_futex_lock_pi },
75 { "all", "Run all futex benchmarks", NULL },
76 { NULL, NULL, NULL }
77 };
78
79 #ifdef HAVE_EVENTFD_SUPPORT
80 static struct bench epoll_benchmarks[] = {
81 { "wait", "Benchmark epoll concurrent epoll_waits", bench_epoll_wait },
82 { "ctl", "Benchmark epoll concurrent epoll_ctls", bench_epoll_ctl },
83 { "all", "Run all futex benchmarks", NULL },
84 { NULL, NULL, NULL }
85 };
86 #endif // HAVE_EVENTFD_SUPPORT
87
88 static struct bench internals_benchmarks[] = {
89 { "synthesize", "Benchmark perf event synthesis", bench_synthesize },
90 { "kallsyms-parse", "Benchmark kallsyms parsing", bench_kallsyms_parse },
91 { "inject-build-id", "Benchmark build-id injection", bench_inject_build_id },
92 { "evlist-open-close", "Benchmark evlist open and close", bench_evlist_open_close },
93 { NULL, NULL, NULL }
94 };
95
96 struct collection {
97 const char *name;
98 const char *summary;
99 struct bench *benchmarks;
100 };
101
102 static struct collection collections[] = {
103 { "sched", "Scheduler and IPC benchmarks", sched_benchmarks },
104 { "syscall", "System call benchmarks", syscall_benchmarks },
105 { "mem", "Memory access benchmarks", mem_benchmarks },
106 #ifdef HAVE_LIBNUMA_SUPPORT
107 { "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks },
108 #endif
109 {"futex", "Futex stressing benchmarks", futex_benchmarks },
110 #ifdef HAVE_EVENTFD_SUPPORT
111 {"epoll", "Epoll stressing benchmarks", epoll_benchmarks },
112 #endif
113 { "internals", "Perf-internals benchmarks", internals_benchmarks },
114 { "all", "All benchmarks", NULL },
115 { NULL, NULL, NULL }
116 };
117
118 /* Iterate over all benchmark collections: */
119 #define for_each_collection(coll) \
120 for (coll = collections; coll->name; coll++)
121
122 /* Iterate over all benchmarks within a collection: */
123 #define for_each_bench(coll, bench) \
124 for (bench = coll->benchmarks; bench && bench->name; bench++)
125
dump_benchmarks(struct collection * coll)126 static void dump_benchmarks(struct collection *coll)
127 {
128 struct bench *bench;
129
130 printf("\n # List of available benchmarks for collection '%s':\n\n", coll->name);
131
132 for_each_bench(coll, bench)
133 printf("%14s: %s\n", bench->name, bench->summary);
134
135 printf("\n");
136 }
137
138 static const char *bench_format_str;
139
140 /* Output/formatting style, exported to benchmark modules: */
141 int bench_format = BENCH_FORMAT_DEFAULT;
142 unsigned int bench_repeat = 10; /* default number of times to repeat the run */
143
144 static const struct option bench_options[] = {
145 OPT_STRING('f', "format", &bench_format_str, "default|simple", "Specify the output formatting style"),
146 OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"),
147 OPT_END()
148 };
149
150 static const char * const bench_usage[] = {
151 "perf bench [<common options>] <collection> <benchmark> [<options>]",
152 NULL
153 };
154
print_usage(void)155 static void print_usage(void)
156 {
157 struct collection *coll;
158 int i;
159
160 printf("Usage: \n");
161 for (i = 0; bench_usage[i]; i++)
162 printf("\t%s\n", bench_usage[i]);
163 printf("\n");
164
165 printf(" # List of all available benchmark collections:\n\n");
166
167 for_each_collection(coll)
168 printf("%14s: %s\n", coll->name, coll->summary);
169 printf("\n");
170 }
171
bench_str2int(const char * str)172 static int bench_str2int(const char *str)
173 {
174 if (!str)
175 return BENCH_FORMAT_DEFAULT;
176
177 if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR))
178 return BENCH_FORMAT_DEFAULT;
179 else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR))
180 return BENCH_FORMAT_SIMPLE;
181
182 return BENCH_FORMAT_UNKNOWN;
183 }
184
185 /*
186 * Run a specific benchmark but first rename the running task's ->comm[]
187 * to something meaningful:
188 */
run_bench(const char * coll_name,const char * bench_name,bench_fn_t fn,int argc,const char ** argv)189 static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t fn,
190 int argc, const char **argv)
191 {
192 int size;
193 char *name;
194 int ret;
195
196 size = strlen(coll_name) + 1 + strlen(bench_name) + 1;
197
198 name = zalloc(size);
199 BUG_ON(!name);
200
201 scnprintf(name, size, "%s-%s", coll_name, bench_name);
202
203 prctl(PR_SET_NAME, name);
204 argv[0] = name;
205
206 ret = fn(argc, argv);
207
208 free(name);
209
210 return ret;
211 }
212
run_collection(struct collection * coll)213 static void run_collection(struct collection *coll)
214 {
215 struct bench *bench;
216 const char *argv[2];
217
218 argv[1] = NULL;
219 /*
220 * TODO:
221 *
222 * Preparing preset parameters for
223 * embedded, ordinary PC, HPC, etc...
224 * would be helpful.
225 */
226 for_each_bench(coll, bench) {
227 if (!bench->fn)
228 break;
229 printf("# Running %s/%s benchmark...\n", coll->name, bench->name);
230
231 argv[1] = bench->name;
232 run_bench(coll->name, bench->name, bench->fn, 1, argv);
233 printf("\n");
234 }
235 }
236
run_all_collections(void)237 static void run_all_collections(void)
238 {
239 struct collection *coll;
240
241 for_each_collection(coll)
242 run_collection(coll);
243 }
244
cmd_bench(int argc,const char ** argv)245 int cmd_bench(int argc, const char **argv)
246 {
247 struct collection *coll;
248 int ret = 0;
249
250 /* Unbuffered output */
251 setvbuf(stdout, NULL, _IONBF, 0);
252 setlocale(LC_ALL, "");
253
254 if (argc < 2) {
255 /* No collection specified. */
256 print_usage();
257 goto end;
258 }
259
260 argc = parse_options(argc, argv, bench_options, bench_usage,
261 PARSE_OPT_STOP_AT_NON_OPTION);
262
263 bench_format = bench_str2int(bench_format_str);
264 if (bench_format == BENCH_FORMAT_UNKNOWN) {
265 printf("Unknown format descriptor: '%s'\n", bench_format_str);
266 goto end;
267 }
268
269 if (bench_repeat == 0) {
270 printf("Invalid repeat option: Must specify a positive value\n");
271 goto end;
272 }
273
274 if (argc < 1) {
275 print_usage();
276 goto end;
277 }
278
279 if (!strcmp(argv[0], "all")) {
280 run_all_collections();
281 goto end;
282 }
283
284 for_each_collection(coll) {
285 struct bench *bench;
286
287 if (strcmp(coll->name, argv[0]))
288 continue;
289
290 if (argc < 2) {
291 /* No bench specified. */
292 dump_benchmarks(coll);
293 goto end;
294 }
295
296 if (!strcmp(argv[1], "all")) {
297 run_collection(coll);
298 goto end;
299 }
300
301 for_each_bench(coll, bench) {
302 if (strcmp(bench->name, argv[1]))
303 continue;
304
305 if (bench_format == BENCH_FORMAT_DEFAULT)
306 printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name);
307 ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1);
308 goto end;
309 }
310
311 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
312 dump_benchmarks(coll);
313 goto end;
314 }
315
316 printf("Unknown benchmark: '%s' for collection '%s'\n", argv[1], argv[0]);
317 ret = 1;
318 goto end;
319 }
320
321 printf("Unknown collection: '%s'\n", argv[0]);
322 ret = 1;
323
324 end:
325 return ret;
326 }
327