| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * builtin-bench.c | 
 |  * | 
 |  * General benchmarking collections provided by perf | 
 |  * | 
 |  * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | 
 |  */ | 
 |  | 
 | /* | 
 |  * Available benchmark collection list: | 
 |  * | 
 |  *  sched ... scheduler and IPC performance | 
 |  *  mem   ... memory access performance | 
 |  *  numa  ... NUMA scheduling and MM performance | 
 |  *  futex ... Futex performance | 
 |  *  epoll ... Event poll performance | 
 |  */ | 
 | #include <subcmd/parse-options.h> | 
 | #include "builtin.h" | 
 | #include "bench/bench.h" | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/prctl.h> | 
 | #include <linux/zalloc.h> | 
 |  | 
 | typedef int (*bench_fn_t)(int argc, const char **argv); | 
 |  | 
 | struct bench { | 
 | 	const char	*name; | 
 | 	const char	*summary; | 
 | 	bench_fn_t	fn; | 
 | }; | 
 |  | 
 | #ifdef HAVE_LIBNUMA_SUPPORT | 
 | static struct bench numa_benchmarks[] = { | 
 | 	{ "mem",	"Benchmark for NUMA workloads",			bench_numa		}, | 
 | 	{ "all",	"Run all NUMA benchmarks",			NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 | #endif | 
 |  | 
 | static struct bench sched_benchmarks[] = { | 
 | 	{ "messaging",	"Benchmark for scheduling and IPC",		bench_sched_messaging	}, | 
 | 	{ "pipe",	"Benchmark for pipe() between two processes",	bench_sched_pipe	}, | 
 | 	{ "all",	"Run all scheduler benchmarks",		NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 |  | 
 | static struct bench mem_benchmarks[] = { | 
 | 	{ "memcpy",	"Benchmark for memcpy() functions",		bench_mem_memcpy	}, | 
 | 	{ "memset",	"Benchmark for memset() functions",		bench_mem_memset	}, | 
 | 	{ "all",	"Run all memory access benchmarks",		NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 |  | 
 | static struct bench futex_benchmarks[] = { | 
 | 	{ "hash",	"Benchmark for futex hash table",               bench_futex_hash	}, | 
 | 	{ "wake",	"Benchmark for futex wake calls",               bench_futex_wake	}, | 
 | 	{ "wake-parallel", "Benchmark for parallel futex wake calls",   bench_futex_wake_parallel }, | 
 | 	{ "requeue",	"Benchmark for futex requeue calls",            bench_futex_requeue	}, | 
 | 	/* pi-futexes */ | 
 | 	{ "lock-pi",	"Benchmark for futex lock_pi calls",            bench_futex_lock_pi	}, | 
 | 	{ "all",	"Run all futex benchmarks",			NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 |  | 
 | #ifdef HAVE_EVENTFD | 
 | static struct bench epoll_benchmarks[] = { | 
 | 	{ "wait",	"Benchmark epoll concurrent epoll_waits",       bench_epoll_wait	}, | 
 | 	{ "ctl",	"Benchmark epoll concurrent epoll_ctls",        bench_epoll_ctl		}, | 
 | 	{ "all",	"Run all futex benchmarks",			NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 | #endif // HAVE_EVENTFD | 
 |  | 
 | struct collection { | 
 | 	const char	*name; | 
 | 	const char	*summary; | 
 | 	struct bench	*benchmarks; | 
 | }; | 
 |  | 
 | static struct collection collections[] = { | 
 | 	{ "sched",	"Scheduler and IPC benchmarks",			sched_benchmarks	}, | 
 | 	{ "mem",	"Memory access benchmarks",			mem_benchmarks		}, | 
 | #ifdef HAVE_LIBNUMA_SUPPORT | 
 | 	{ "numa",	"NUMA scheduling and MM benchmarks",		numa_benchmarks		}, | 
 | #endif | 
 | 	{"futex",       "Futex stressing benchmarks",                   futex_benchmarks        }, | 
 | #ifdef HAVE_EVENTFD | 
 | 	{"epoll",       "Epoll stressing benchmarks",                   epoll_benchmarks        }, | 
 | #endif | 
 | 	{ "all",	"All benchmarks",				NULL			}, | 
 | 	{ NULL,		NULL,						NULL			} | 
 | }; | 
 |  | 
 | /* Iterate over all benchmark collections: */ | 
 | #define for_each_collection(coll) \ | 
 | 	for (coll = collections; coll->name; coll++) | 
 |  | 
 | /* Iterate over all benchmarks within a collection: */ | 
 | #define for_each_bench(coll, bench) \ | 
 | 	for (bench = coll->benchmarks; bench && bench->name; bench++) | 
 |  | 
 | static void dump_benchmarks(struct collection *coll) | 
 | { | 
 | 	struct bench *bench; | 
 |  | 
 | 	printf("\n        # List of available benchmarks for collection '%s':\n\n", coll->name); | 
 |  | 
 | 	for_each_bench(coll, bench) | 
 | 		printf("%14s: %s\n", bench->name, bench->summary); | 
 |  | 
 | 	printf("\n"); | 
 | } | 
 |  | 
 | static const char *bench_format_str; | 
 |  | 
 | /* Output/formatting style, exported to benchmark modules: */ | 
 | int bench_format = BENCH_FORMAT_DEFAULT; | 
 | unsigned int bench_repeat = 10; /* default number of times to repeat the run */ | 
 |  | 
 | static const struct option bench_options[] = { | 
 | 	OPT_STRING('f', "format", &bench_format_str, "default|simple", "Specify the output formatting style"), | 
 | 	OPT_UINTEGER('r', "repeat",  &bench_repeat,   "Specify amount of times to repeat the run"), | 
 | 	OPT_END() | 
 | }; | 
 |  | 
 | static const char * const bench_usage[] = { | 
 | 	"perf bench [<common options>] <collection> <benchmark> [<options>]", | 
 | 	NULL | 
 | }; | 
 |  | 
 | static void print_usage(void) | 
 | { | 
 | 	struct collection *coll; | 
 | 	int i; | 
 |  | 
 | 	printf("Usage: \n"); | 
 | 	for (i = 0; bench_usage[i]; i++) | 
 | 		printf("\t%s\n", bench_usage[i]); | 
 | 	printf("\n"); | 
 |  | 
 | 	printf("        # List of all available benchmark collections:\n\n"); | 
 |  | 
 | 	for_each_collection(coll) | 
 | 		printf("%14s: %s\n", coll->name, coll->summary); | 
 | 	printf("\n"); | 
 | } | 
 |  | 
 | static int bench_str2int(const char *str) | 
 | { | 
 | 	if (!str) | 
 | 		return BENCH_FORMAT_DEFAULT; | 
 |  | 
 | 	if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR)) | 
 | 		return BENCH_FORMAT_DEFAULT; | 
 | 	else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR)) | 
 | 		return BENCH_FORMAT_SIMPLE; | 
 |  | 
 | 	return BENCH_FORMAT_UNKNOWN; | 
 | } | 
 |  | 
 | /* | 
 |  * Run a specific benchmark but first rename the running task's ->comm[] | 
 |  * to something meaningful: | 
 |  */ | 
 | static int run_bench(const char *coll_name, const char *bench_name, bench_fn_t fn, | 
 | 		     int argc, const char **argv) | 
 | { | 
 | 	int size; | 
 | 	char *name; | 
 | 	int ret; | 
 |  | 
 | 	size = strlen(coll_name) + 1 + strlen(bench_name) + 1; | 
 |  | 
 | 	name = zalloc(size); | 
 | 	BUG_ON(!name); | 
 |  | 
 | 	scnprintf(name, size, "%s-%s", coll_name, bench_name); | 
 |  | 
 | 	prctl(PR_SET_NAME, name); | 
 | 	argv[0] = name; | 
 |  | 
 | 	ret = fn(argc, argv); | 
 |  | 
 | 	free(name); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void run_collection(struct collection *coll) | 
 | { | 
 | 	struct bench *bench; | 
 | 	const char *argv[2]; | 
 |  | 
 | 	argv[1] = NULL; | 
 | 	/* | 
 | 	 * TODO: | 
 | 	 * | 
 | 	 * Preparing preset parameters for | 
 | 	 * embedded, ordinary PC, HPC, etc... | 
 | 	 * would be helpful. | 
 | 	 */ | 
 | 	for_each_bench(coll, bench) { | 
 | 		if (!bench->fn) | 
 | 			break; | 
 | 		printf("# Running %s/%s benchmark...\n", coll->name, bench->name); | 
 | 		fflush(stdout); | 
 |  | 
 | 		argv[1] = bench->name; | 
 | 		run_bench(coll->name, bench->name, bench->fn, 1, argv); | 
 | 		printf("\n"); | 
 | 	} | 
 | } | 
 |  | 
 | static void run_all_collections(void) | 
 | { | 
 | 	struct collection *coll; | 
 |  | 
 | 	for_each_collection(coll) | 
 | 		run_collection(coll); | 
 | } | 
 |  | 
 | int cmd_bench(int argc, const char **argv) | 
 | { | 
 | 	struct collection *coll; | 
 | 	int ret = 0; | 
 |  | 
 | 	if (argc < 2) { | 
 | 		/* No collection specified. */ | 
 | 		print_usage(); | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	argc = parse_options(argc, argv, bench_options, bench_usage, | 
 | 			     PARSE_OPT_STOP_AT_NON_OPTION); | 
 |  | 
 | 	bench_format = bench_str2int(bench_format_str); | 
 | 	if (bench_format == BENCH_FORMAT_UNKNOWN) { | 
 | 		printf("Unknown format descriptor: '%s'\n", bench_format_str); | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	if (bench_repeat == 0) { | 
 | 		printf("Invalid repeat option: Must specify a positive value\n"); | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	if (argc < 1) { | 
 | 		print_usage(); | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	if (!strcmp(argv[0], "all")) { | 
 | 		run_all_collections(); | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	for_each_collection(coll) { | 
 | 		struct bench *bench; | 
 |  | 
 | 		if (strcmp(coll->name, argv[0])) | 
 | 			continue; | 
 |  | 
 | 		if (argc < 2) { | 
 | 			/* No bench specified. */ | 
 | 			dump_benchmarks(coll); | 
 | 			goto end; | 
 | 		} | 
 |  | 
 | 		if (!strcmp(argv[1], "all")) { | 
 | 			run_collection(coll); | 
 | 			goto end; | 
 | 		} | 
 |  | 
 | 		for_each_bench(coll, bench) { | 
 | 			if (strcmp(bench->name, argv[1])) | 
 | 				continue; | 
 |  | 
 | 			if (bench_format == BENCH_FORMAT_DEFAULT) | 
 | 				printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name); | 
 | 			fflush(stdout); | 
 | 			ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1); | 
 | 			goto end; | 
 | 		} | 
 |  | 
 | 		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { | 
 | 			dump_benchmarks(coll); | 
 | 			goto end; | 
 | 		} | 
 |  | 
 | 		printf("Unknown benchmark: '%s' for collection '%s'\n", argv[1], argv[0]); | 
 | 		ret = 1; | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	printf("Unknown collection: '%s'\n", argv[0]); | 
 | 	ret = 1; | 
 |  | 
 | end: | 
 | 	return ret; | 
 | } |