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