162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2020 Facebook */
362306a36Sopenharmony_ci#define _GNU_SOURCE
462306a36Sopenharmony_ci#include <argp.h>
562306a36Sopenharmony_ci#include <linux/compiler.h>
662306a36Sopenharmony_ci#include <sys/time.h>
762306a36Sopenharmony_ci#include <sched.h>
862306a36Sopenharmony_ci#include <fcntl.h>
962306a36Sopenharmony_ci#include <pthread.h>
1062306a36Sopenharmony_ci#include <sys/sysinfo.h>
1162306a36Sopenharmony_ci#include <signal.h>
1262306a36Sopenharmony_ci#include "bench.h"
1362306a36Sopenharmony_ci#include "testing_helpers.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct env env = {
1662306a36Sopenharmony_ci	.warmup_sec = 1,
1762306a36Sopenharmony_ci	.duration_sec = 5,
1862306a36Sopenharmony_ci	.affinity = false,
1962306a36Sopenharmony_ci	.quiet = false,
2062306a36Sopenharmony_ci	.consumer_cnt = 0,
2162306a36Sopenharmony_ci	.producer_cnt = 1,
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int libbpf_print_fn(enum libbpf_print_level level,
2562306a36Sopenharmony_ci		    const char *format, va_list args)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	if (level == LIBBPF_DEBUG && !env.verbose)
2862306a36Sopenharmony_ci		return 0;
2962306a36Sopenharmony_ci	return vfprintf(stderr, format, args);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid setup_libbpf(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
3562306a36Sopenharmony_ci	libbpf_set_print(libbpf_print_fn);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid false_hits_report_progress(int iter, struct bench_res *res, long delta_ns)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	long total = res->false_hits  + res->hits + res->drops;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	printf("Iter %3d (%7.3lfus): ",
4362306a36Sopenharmony_ci	       iter, (delta_ns - 1000000000) / 1000.0);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	printf("%ld false hits of %ld total operations. Percentage = %2.2f %%\n",
4662306a36Sopenharmony_ci	       res->false_hits, total, ((float)res->false_hits / total) * 100);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid false_hits_report_final(struct bench_res res[], int res_cnt)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	long total_hits = 0, total_drops = 0, total_false_hits = 0, total_ops = 0;
5262306a36Sopenharmony_ci	int i;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++) {
5562306a36Sopenharmony_ci		total_hits += res[i].hits;
5662306a36Sopenharmony_ci		total_false_hits += res[i].false_hits;
5762306a36Sopenharmony_ci		total_drops += res[i].drops;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	total_ops = total_hits + total_false_hits + total_drops;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	printf("Summary: %ld false hits of %ld total operations. ",
6262306a36Sopenharmony_ci	       total_false_hits, total_ops);
6362306a36Sopenharmony_ci	printf("Percentage =  %2.2f %%\n",
6462306a36Sopenharmony_ci	       ((float)total_false_hits / total_ops) * 100);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_civoid hits_drops_report_progress(int iter, struct bench_res *res, long delta_ns)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	double hits_per_sec, drops_per_sec;
7062306a36Sopenharmony_ci	double hits_per_prod;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	hits_per_sec = res->hits / 1000000.0 / (delta_ns / 1000000000.0);
7362306a36Sopenharmony_ci	hits_per_prod = hits_per_sec / env.producer_cnt;
7462306a36Sopenharmony_ci	drops_per_sec = res->drops / 1000000.0 / (delta_ns / 1000000000.0);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	printf("Iter %3d (%7.3lfus): ",
7762306a36Sopenharmony_ci	       iter, (delta_ns - 1000000000) / 1000.0);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	printf("hits %8.3lfM/s (%7.3lfM/prod), drops %8.3lfM/s, total operations %8.3lfM/s\n",
8062306a36Sopenharmony_ci	       hits_per_sec, hits_per_prod, drops_per_sec, hits_per_sec + drops_per_sec);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid
8462306a36Sopenharmony_cigrace_period_latency_basic_stats(struct bench_res res[], int res_cnt, struct basic_stats *gp_stat)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int i;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	memset(gp_stat, 0, sizeof(struct basic_stats));
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++)
9162306a36Sopenharmony_ci		gp_stat->mean += res[i].gp_ns / 1000.0 / (double)res[i].gp_ct / (0.0 + res_cnt);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define IT_MEAN_DIFF (res[i].gp_ns / 1000.0 / (double)res[i].gp_ct - gp_stat->mean)
9462306a36Sopenharmony_ci	if (res_cnt > 1) {
9562306a36Sopenharmony_ci		for (i = 0; i < res_cnt; i++)
9662306a36Sopenharmony_ci			gp_stat->stddev += (IT_MEAN_DIFF * IT_MEAN_DIFF) / (res_cnt - 1.0);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	gp_stat->stddev = sqrt(gp_stat->stddev);
9962306a36Sopenharmony_ci#undef IT_MEAN_DIFF
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid
10362306a36Sopenharmony_cigrace_period_ticks_basic_stats(struct bench_res res[], int res_cnt, struct basic_stats *gp_stat)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int i;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	memset(gp_stat, 0, sizeof(struct basic_stats));
10862306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++)
10962306a36Sopenharmony_ci		gp_stat->mean += res[i].stime / (double)res[i].gp_ct / (0.0 + res_cnt);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define IT_MEAN_DIFF (res[i].stime / (double)res[i].gp_ct - gp_stat->mean)
11262306a36Sopenharmony_ci	if (res_cnt > 1) {
11362306a36Sopenharmony_ci		for (i = 0; i < res_cnt; i++)
11462306a36Sopenharmony_ci			gp_stat->stddev += (IT_MEAN_DIFF * IT_MEAN_DIFF) / (res_cnt - 1.0);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	gp_stat->stddev = sqrt(gp_stat->stddev);
11762306a36Sopenharmony_ci#undef IT_MEAN_DIFF
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_civoid hits_drops_report_final(struct bench_res res[], int res_cnt)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int i;
12362306a36Sopenharmony_ci	double hits_mean = 0.0, drops_mean = 0.0, total_ops_mean = 0.0;
12462306a36Sopenharmony_ci	double hits_stddev = 0.0, drops_stddev = 0.0, total_ops_stddev = 0.0;
12562306a36Sopenharmony_ci	double total_ops;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++) {
12862306a36Sopenharmony_ci		hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
12962306a36Sopenharmony_ci		drops_mean += res[i].drops / 1000000.0 / (0.0 + res_cnt);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	total_ops_mean = hits_mean + drops_mean;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (res_cnt > 1)  {
13462306a36Sopenharmony_ci		for (i = 0; i < res_cnt; i++) {
13562306a36Sopenharmony_ci			hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
13662306a36Sopenharmony_ci				       (hits_mean - res[i].hits / 1000000.0) /
13762306a36Sopenharmony_ci				       (res_cnt - 1.0);
13862306a36Sopenharmony_ci			drops_stddev += (drops_mean - res[i].drops / 1000000.0) *
13962306a36Sopenharmony_ci					(drops_mean - res[i].drops / 1000000.0) /
14062306a36Sopenharmony_ci					(res_cnt - 1.0);
14162306a36Sopenharmony_ci			total_ops = res[i].hits + res[i].drops;
14262306a36Sopenharmony_ci			total_ops_stddev += (total_ops_mean - total_ops / 1000000.0) *
14362306a36Sopenharmony_ci					(total_ops_mean - total_ops / 1000000.0) /
14462306a36Sopenharmony_ci					(res_cnt - 1.0);
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		hits_stddev = sqrt(hits_stddev);
14762306a36Sopenharmony_ci		drops_stddev = sqrt(drops_stddev);
14862306a36Sopenharmony_ci		total_ops_stddev = sqrt(total_ops_stddev);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	printf("Summary: hits %8.3lf \u00B1 %5.3lfM/s (%7.3lfM/prod), ",
15162306a36Sopenharmony_ci	       hits_mean, hits_stddev, hits_mean / env.producer_cnt);
15262306a36Sopenharmony_ci	printf("drops %8.3lf \u00B1 %5.3lfM/s, ",
15362306a36Sopenharmony_ci	       drops_mean, drops_stddev);
15462306a36Sopenharmony_ci	printf("total operations %8.3lf \u00B1 %5.3lfM/s\n",
15562306a36Sopenharmony_ci	       total_ops_mean, total_ops_stddev);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_civoid ops_report_progress(int iter, struct bench_res *res, long delta_ns)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	double hits_per_sec, hits_per_prod;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	hits_per_sec = res->hits / 1000000.0 / (delta_ns / 1000000000.0);
16362306a36Sopenharmony_ci	hits_per_prod = hits_per_sec / env.producer_cnt;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	printf("hits %8.3lfM/s (%7.3lfM/prod)\n", hits_per_sec, hits_per_prod);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_civoid ops_report_final(struct bench_res res[], int res_cnt)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	double hits_mean = 0.0, hits_stddev = 0.0;
17362306a36Sopenharmony_ci	int i;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++)
17662306a36Sopenharmony_ci		hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (res_cnt > 1)  {
17962306a36Sopenharmony_ci		for (i = 0; i < res_cnt; i++)
18062306a36Sopenharmony_ci			hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
18162306a36Sopenharmony_ci				       (hits_mean - res[i].hits / 1000000.0) /
18262306a36Sopenharmony_ci				       (res_cnt - 1.0);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		hits_stddev = sqrt(hits_stddev);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	printf("Summary: throughput %8.3lf \u00B1 %5.3lf M ops/s (%7.3lfM ops/prod), ",
18762306a36Sopenharmony_ci	       hits_mean, hits_stddev, hits_mean / env.producer_cnt);
18862306a36Sopenharmony_ci	printf("latency %8.3lf ns/op\n", 1000.0 / hits_mean * env.producer_cnt);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid local_storage_report_progress(int iter, struct bench_res *res,
19262306a36Sopenharmony_ci				   long delta_ns)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	double important_hits_per_sec, hits_per_sec;
19562306a36Sopenharmony_ci	double delta_sec = delta_ns / 1000000000.0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	hits_per_sec = res->hits / 1000000.0 / delta_sec;
19862306a36Sopenharmony_ci	important_hits_per_sec = res->important_hits / 1000000.0 / delta_sec;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	printf("hits %8.3lfM/s ", hits_per_sec);
20362306a36Sopenharmony_ci	printf("important_hits %8.3lfM/s\n", important_hits_per_sec);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_civoid local_storage_report_final(struct bench_res res[], int res_cnt)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	double important_hits_mean = 0.0, important_hits_stddev = 0.0;
20962306a36Sopenharmony_ci	double hits_mean = 0.0, hits_stddev = 0.0;
21062306a36Sopenharmony_ci	int i;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	for (i = 0; i < res_cnt; i++) {
21362306a36Sopenharmony_ci		hits_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt);
21462306a36Sopenharmony_ci		important_hits_mean += res[i].important_hits / 1000000.0 / (0.0 + res_cnt);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (res_cnt > 1)  {
21862306a36Sopenharmony_ci		for (i = 0; i < res_cnt; i++) {
21962306a36Sopenharmony_ci			hits_stddev += (hits_mean - res[i].hits / 1000000.0) *
22062306a36Sopenharmony_ci				       (hits_mean - res[i].hits / 1000000.0) /
22162306a36Sopenharmony_ci				       (res_cnt - 1.0);
22262306a36Sopenharmony_ci			important_hits_stddev +=
22362306a36Sopenharmony_ci				       (important_hits_mean - res[i].important_hits / 1000000.0) *
22462306a36Sopenharmony_ci				       (important_hits_mean - res[i].important_hits / 1000000.0) /
22562306a36Sopenharmony_ci				       (res_cnt - 1.0);
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		hits_stddev = sqrt(hits_stddev);
22962306a36Sopenharmony_ci		important_hits_stddev = sqrt(important_hits_stddev);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	printf("Summary: hits throughput %8.3lf \u00B1 %5.3lf M ops/s, ",
23262306a36Sopenharmony_ci	       hits_mean, hits_stddev);
23362306a36Sopenharmony_ci	printf("hits latency %8.3lf ns/op, ", 1000.0 / hits_mean);
23462306a36Sopenharmony_ci	printf("important_hits throughput %8.3lf \u00B1 %5.3lf M ops/s\n",
23562306a36Sopenharmony_ci	       important_hits_mean, important_hits_stddev);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciconst char *argp_program_version = "benchmark";
23962306a36Sopenharmony_ciconst char *argp_program_bug_address = "<bpf@vger.kernel.org>";
24062306a36Sopenharmony_ciconst char argp_program_doc[] =
24162306a36Sopenharmony_ci"benchmark    Generic benchmarking framework.\n"
24262306a36Sopenharmony_ci"\n"
24362306a36Sopenharmony_ci"This tool runs benchmarks.\n"
24462306a36Sopenharmony_ci"\n"
24562306a36Sopenharmony_ci"USAGE: benchmark <bench-name>\n"
24662306a36Sopenharmony_ci"\n"
24762306a36Sopenharmony_ci"EXAMPLES:\n"
24862306a36Sopenharmony_ci"    # run 'count-local' benchmark with 1 producer and 1 consumer\n"
24962306a36Sopenharmony_ci"    benchmark count-local\n"
25062306a36Sopenharmony_ci"    # run 'count-local' with 16 producer and 8 consumer thread, pinned to CPUs\n"
25162306a36Sopenharmony_ci"    benchmark -p16 -c8 -a count-local\n";
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cienum {
25462306a36Sopenharmony_ci	ARG_PROD_AFFINITY_SET = 1000,
25562306a36Sopenharmony_ci	ARG_CONS_AFFINITY_SET = 1001,
25662306a36Sopenharmony_ci};
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic const struct argp_option opts[] = {
25962306a36Sopenharmony_ci	{ "list", 'l', NULL, 0, "List available benchmarks"},
26062306a36Sopenharmony_ci	{ "duration", 'd', "SEC", 0, "Duration of benchmark, seconds"},
26162306a36Sopenharmony_ci	{ "warmup", 'w', "SEC", 0, "Warm-up period, seconds"},
26262306a36Sopenharmony_ci	{ "producers", 'p', "NUM", 0, "Number of producer threads"},
26362306a36Sopenharmony_ci	{ "consumers", 'c', "NUM", 0, "Number of consumer threads"},
26462306a36Sopenharmony_ci	{ "verbose", 'v', NULL, 0, "Verbose debug output"},
26562306a36Sopenharmony_ci	{ "affinity", 'a', NULL, 0, "Set consumer/producer thread affinity"},
26662306a36Sopenharmony_ci	{ "quiet", 'q', NULL, 0, "Be more quiet"},
26762306a36Sopenharmony_ci	{ "prod-affinity", ARG_PROD_AFFINITY_SET, "CPUSET", 0,
26862306a36Sopenharmony_ci	  "Set of CPUs for producer threads; implies --affinity"},
26962306a36Sopenharmony_ci	{ "cons-affinity", ARG_CONS_AFFINITY_SET, "CPUSET", 0,
27062306a36Sopenharmony_ci	  "Set of CPUs for consumer threads; implies --affinity"},
27162306a36Sopenharmony_ci	{},
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ciextern struct argp bench_ringbufs_argp;
27562306a36Sopenharmony_ciextern struct argp bench_bloom_map_argp;
27662306a36Sopenharmony_ciextern struct argp bench_bpf_loop_argp;
27762306a36Sopenharmony_ciextern struct argp bench_local_storage_argp;
27862306a36Sopenharmony_ciextern struct argp bench_local_storage_rcu_tasks_trace_argp;
27962306a36Sopenharmony_ciextern struct argp bench_strncmp_argp;
28062306a36Sopenharmony_ciextern struct argp bench_hashmap_lookup_argp;
28162306a36Sopenharmony_ciextern struct argp bench_local_storage_create_argp;
28262306a36Sopenharmony_ciextern struct argp bench_htab_mem_argp;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic const struct argp_child bench_parsers[] = {
28562306a36Sopenharmony_ci	{ &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 },
28662306a36Sopenharmony_ci	{ &bench_bloom_map_argp, 0, "Bloom filter map benchmark", 0 },
28762306a36Sopenharmony_ci	{ &bench_bpf_loop_argp, 0, "bpf_loop helper benchmark", 0 },
28862306a36Sopenharmony_ci	{ &bench_local_storage_argp, 0, "local_storage benchmark", 0 },
28962306a36Sopenharmony_ci	{ &bench_strncmp_argp, 0, "bpf_strncmp helper benchmark", 0 },
29062306a36Sopenharmony_ci	{ &bench_local_storage_rcu_tasks_trace_argp, 0,
29162306a36Sopenharmony_ci		"local_storage RCU Tasks Trace slowdown benchmark", 0 },
29262306a36Sopenharmony_ci	{ &bench_hashmap_lookup_argp, 0, "Hashmap lookup benchmark", 0 },
29362306a36Sopenharmony_ci	{ &bench_local_storage_create_argp, 0, "local-storage-create benchmark", 0 },
29462306a36Sopenharmony_ci	{ &bench_htab_mem_argp, 0, "hash map memory benchmark", 0 },
29562306a36Sopenharmony_ci	{},
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/* Make pos_args global, so that we can run argp_parse twice, if necessary */
29962306a36Sopenharmony_cistatic int pos_args;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic error_t parse_arg(int key, char *arg, struct argp_state *state)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	switch (key) {
30462306a36Sopenharmony_ci	case 'v':
30562306a36Sopenharmony_ci		env.verbose = true;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case 'l':
30862306a36Sopenharmony_ci		env.list = true;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	case 'd':
31162306a36Sopenharmony_ci		env.duration_sec = strtol(arg, NULL, 10);
31262306a36Sopenharmony_ci		if (env.duration_sec <= 0) {
31362306a36Sopenharmony_ci			fprintf(stderr, "Invalid duration: %s\n", arg);
31462306a36Sopenharmony_ci			argp_usage(state);
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	case 'w':
31862306a36Sopenharmony_ci		env.warmup_sec = strtol(arg, NULL, 10);
31962306a36Sopenharmony_ci		if (env.warmup_sec <= 0) {
32062306a36Sopenharmony_ci			fprintf(stderr, "Invalid warm-up duration: %s\n", arg);
32162306a36Sopenharmony_ci			argp_usage(state);
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		break;
32462306a36Sopenharmony_ci	case 'p':
32562306a36Sopenharmony_ci		env.producer_cnt = strtol(arg, NULL, 10);
32662306a36Sopenharmony_ci		if (env.producer_cnt <= 0) {
32762306a36Sopenharmony_ci			fprintf(stderr, "Invalid producer count: %s\n", arg);
32862306a36Sopenharmony_ci			argp_usage(state);
32962306a36Sopenharmony_ci		}
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	case 'c':
33262306a36Sopenharmony_ci		env.consumer_cnt = strtol(arg, NULL, 10);
33362306a36Sopenharmony_ci		if (env.consumer_cnt <= 0) {
33462306a36Sopenharmony_ci			fprintf(stderr, "Invalid consumer count: %s\n", arg);
33562306a36Sopenharmony_ci			argp_usage(state);
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case 'a':
33962306a36Sopenharmony_ci		env.affinity = true;
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	case 'q':
34262306a36Sopenharmony_ci		env.quiet = true;
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci	case ARG_PROD_AFFINITY_SET:
34562306a36Sopenharmony_ci		env.affinity = true;
34662306a36Sopenharmony_ci		if (parse_num_list(arg, &env.prod_cpus.cpus,
34762306a36Sopenharmony_ci				   &env.prod_cpus.cpus_len)) {
34862306a36Sopenharmony_ci			fprintf(stderr, "Invalid format of CPU set for producers.");
34962306a36Sopenharmony_ci			argp_usage(state);
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci	case ARG_CONS_AFFINITY_SET:
35362306a36Sopenharmony_ci		env.affinity = true;
35462306a36Sopenharmony_ci		if (parse_num_list(arg, &env.cons_cpus.cpus,
35562306a36Sopenharmony_ci				   &env.cons_cpus.cpus_len)) {
35662306a36Sopenharmony_ci			fprintf(stderr, "Invalid format of CPU set for consumers.");
35762306a36Sopenharmony_ci			argp_usage(state);
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	case ARGP_KEY_ARG:
36162306a36Sopenharmony_ci		if (pos_args++) {
36262306a36Sopenharmony_ci			fprintf(stderr,
36362306a36Sopenharmony_ci				"Unrecognized positional argument: %s\n", arg);
36462306a36Sopenharmony_ci			argp_usage(state);
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		env.bench_name = strdup(arg);
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	default:
36962306a36Sopenharmony_ci		return ARGP_ERR_UNKNOWN;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci	return 0;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void parse_cmdline_args_init(int argc, char **argv)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	static const struct argp argp = {
37762306a36Sopenharmony_ci		.options = opts,
37862306a36Sopenharmony_ci		.parser = parse_arg,
37962306a36Sopenharmony_ci		.doc = argp_program_doc,
38062306a36Sopenharmony_ci		.children = bench_parsers,
38162306a36Sopenharmony_ci	};
38262306a36Sopenharmony_ci	if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
38362306a36Sopenharmony_ci		exit(1);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void parse_cmdline_args_final(int argc, char **argv)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct argp_child bench_parsers[2] = {};
38962306a36Sopenharmony_ci	const struct argp argp = {
39062306a36Sopenharmony_ci		.options = opts,
39162306a36Sopenharmony_ci		.parser = parse_arg,
39262306a36Sopenharmony_ci		.doc = argp_program_doc,
39362306a36Sopenharmony_ci		.children = bench_parsers,
39462306a36Sopenharmony_ci	};
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Parse arguments the second time with the correct set of parsers */
39762306a36Sopenharmony_ci	if (bench->argp) {
39862306a36Sopenharmony_ci		bench_parsers[0].argp = bench->argp;
39962306a36Sopenharmony_ci		bench_parsers[0].header = bench->name;
40062306a36Sopenharmony_ci		pos_args = 0;
40162306a36Sopenharmony_ci		if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
40262306a36Sopenharmony_ci			exit(1);
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic void collect_measurements(long delta_ns);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic __u64 last_time_ns;
40962306a36Sopenharmony_cistatic void sigalarm_handler(int signo)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	long new_time_ns = get_time_ns();
41262306a36Sopenharmony_ci	long delta_ns = new_time_ns - last_time_ns;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	collect_measurements(delta_ns);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	last_time_ns = new_time_ns;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/* set up periodic 1-second timer */
42062306a36Sopenharmony_cistatic void setup_timer()
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	static struct sigaction sigalarm_action = {
42362306a36Sopenharmony_ci		.sa_handler = sigalarm_handler,
42462306a36Sopenharmony_ci	};
42562306a36Sopenharmony_ci	struct itimerval timer_settings = {};
42662306a36Sopenharmony_ci	int err;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	last_time_ns = get_time_ns();
42962306a36Sopenharmony_ci	err = sigaction(SIGALRM, &sigalarm_action, NULL);
43062306a36Sopenharmony_ci	if (err < 0) {
43162306a36Sopenharmony_ci		fprintf(stderr, "failed to install SIGALRM handler: %d\n", -errno);
43262306a36Sopenharmony_ci		exit(1);
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	timer_settings.it_interval.tv_sec = 1;
43562306a36Sopenharmony_ci	timer_settings.it_value.tv_sec = 1;
43662306a36Sopenharmony_ci	err = setitimer(ITIMER_REAL, &timer_settings, NULL);
43762306a36Sopenharmony_ci	if (err < 0) {
43862306a36Sopenharmony_ci		fprintf(stderr, "failed to arm interval timer: %d\n", -errno);
43962306a36Sopenharmony_ci		exit(1);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void set_thread_affinity(pthread_t thread, int cpu)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	cpu_set_t cpuset;
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	CPU_ZERO(&cpuset);
44962306a36Sopenharmony_ci	CPU_SET(cpu, &cpuset);
45062306a36Sopenharmony_ci	err = pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset);
45162306a36Sopenharmony_ci	if (err) {
45262306a36Sopenharmony_ci		fprintf(stderr, "setting affinity to CPU #%d failed: %d\n",
45362306a36Sopenharmony_ci			cpu, -err);
45462306a36Sopenharmony_ci		exit(1);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int next_cpu(struct cpu_set *cpu_set)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	if (cpu_set->cpus) {
46162306a36Sopenharmony_ci		int i;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		/* find next available CPU */
46462306a36Sopenharmony_ci		for (i = cpu_set->next_cpu; i < cpu_set->cpus_len; i++) {
46562306a36Sopenharmony_ci			if (cpu_set->cpus[i]) {
46662306a36Sopenharmony_ci				cpu_set->next_cpu = i + 1;
46762306a36Sopenharmony_ci				return i;
46862306a36Sopenharmony_ci			}
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci		fprintf(stderr, "Not enough CPUs specified, need CPU #%d or higher.\n", i);
47162306a36Sopenharmony_ci		exit(1);
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return cpu_set->next_cpu++ % env.nr_cpus;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic struct bench_state {
47862306a36Sopenharmony_ci	int res_cnt;
47962306a36Sopenharmony_ci	struct bench_res *results;
48062306a36Sopenharmony_ci	pthread_t *consumers;
48162306a36Sopenharmony_ci	pthread_t *producers;
48262306a36Sopenharmony_ci} state;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ciconst struct bench *bench = NULL;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ciextern const struct bench bench_count_global;
48762306a36Sopenharmony_ciextern const struct bench bench_count_local;
48862306a36Sopenharmony_ciextern const struct bench bench_rename_base;
48962306a36Sopenharmony_ciextern const struct bench bench_rename_kprobe;
49062306a36Sopenharmony_ciextern const struct bench bench_rename_kretprobe;
49162306a36Sopenharmony_ciextern const struct bench bench_rename_rawtp;
49262306a36Sopenharmony_ciextern const struct bench bench_rename_fentry;
49362306a36Sopenharmony_ciextern const struct bench bench_rename_fexit;
49462306a36Sopenharmony_ciextern const struct bench bench_trig_base;
49562306a36Sopenharmony_ciextern const struct bench bench_trig_tp;
49662306a36Sopenharmony_ciextern const struct bench bench_trig_rawtp;
49762306a36Sopenharmony_ciextern const struct bench bench_trig_kprobe;
49862306a36Sopenharmony_ciextern const struct bench bench_trig_fentry;
49962306a36Sopenharmony_ciextern const struct bench bench_trig_fentry_sleep;
50062306a36Sopenharmony_ciextern const struct bench bench_trig_fmodret;
50162306a36Sopenharmony_ciextern const struct bench bench_trig_uprobe_base;
50262306a36Sopenharmony_ciextern const struct bench bench_trig_uprobe_with_nop;
50362306a36Sopenharmony_ciextern const struct bench bench_trig_uretprobe_with_nop;
50462306a36Sopenharmony_ciextern const struct bench bench_trig_uprobe_without_nop;
50562306a36Sopenharmony_ciextern const struct bench bench_trig_uretprobe_without_nop;
50662306a36Sopenharmony_ciextern const struct bench bench_rb_libbpf;
50762306a36Sopenharmony_ciextern const struct bench bench_rb_custom;
50862306a36Sopenharmony_ciextern const struct bench bench_pb_libbpf;
50962306a36Sopenharmony_ciextern const struct bench bench_pb_custom;
51062306a36Sopenharmony_ciextern const struct bench bench_bloom_lookup;
51162306a36Sopenharmony_ciextern const struct bench bench_bloom_update;
51262306a36Sopenharmony_ciextern const struct bench bench_bloom_false_positive;
51362306a36Sopenharmony_ciextern const struct bench bench_hashmap_without_bloom;
51462306a36Sopenharmony_ciextern const struct bench bench_hashmap_with_bloom;
51562306a36Sopenharmony_ciextern const struct bench bench_bpf_loop;
51662306a36Sopenharmony_ciextern const struct bench bench_strncmp_no_helper;
51762306a36Sopenharmony_ciextern const struct bench bench_strncmp_helper;
51862306a36Sopenharmony_ciextern const struct bench bench_bpf_hashmap_full_update;
51962306a36Sopenharmony_ciextern const struct bench bench_local_storage_cache_seq_get;
52062306a36Sopenharmony_ciextern const struct bench bench_local_storage_cache_interleaved_get;
52162306a36Sopenharmony_ciextern const struct bench bench_local_storage_cache_hashmap_control;
52262306a36Sopenharmony_ciextern const struct bench bench_local_storage_tasks_trace;
52362306a36Sopenharmony_ciextern const struct bench bench_bpf_hashmap_lookup;
52462306a36Sopenharmony_ciextern const struct bench bench_local_storage_create;
52562306a36Sopenharmony_ciextern const struct bench bench_htab_mem;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic const struct bench *benchs[] = {
52862306a36Sopenharmony_ci	&bench_count_global,
52962306a36Sopenharmony_ci	&bench_count_local,
53062306a36Sopenharmony_ci	&bench_rename_base,
53162306a36Sopenharmony_ci	&bench_rename_kprobe,
53262306a36Sopenharmony_ci	&bench_rename_kretprobe,
53362306a36Sopenharmony_ci	&bench_rename_rawtp,
53462306a36Sopenharmony_ci	&bench_rename_fentry,
53562306a36Sopenharmony_ci	&bench_rename_fexit,
53662306a36Sopenharmony_ci	&bench_trig_base,
53762306a36Sopenharmony_ci	&bench_trig_tp,
53862306a36Sopenharmony_ci	&bench_trig_rawtp,
53962306a36Sopenharmony_ci	&bench_trig_kprobe,
54062306a36Sopenharmony_ci	&bench_trig_fentry,
54162306a36Sopenharmony_ci	&bench_trig_fentry_sleep,
54262306a36Sopenharmony_ci	&bench_trig_fmodret,
54362306a36Sopenharmony_ci	&bench_trig_uprobe_base,
54462306a36Sopenharmony_ci	&bench_trig_uprobe_with_nop,
54562306a36Sopenharmony_ci	&bench_trig_uretprobe_with_nop,
54662306a36Sopenharmony_ci	&bench_trig_uprobe_without_nop,
54762306a36Sopenharmony_ci	&bench_trig_uretprobe_without_nop,
54862306a36Sopenharmony_ci	&bench_rb_libbpf,
54962306a36Sopenharmony_ci	&bench_rb_custom,
55062306a36Sopenharmony_ci	&bench_pb_libbpf,
55162306a36Sopenharmony_ci	&bench_pb_custom,
55262306a36Sopenharmony_ci	&bench_bloom_lookup,
55362306a36Sopenharmony_ci	&bench_bloom_update,
55462306a36Sopenharmony_ci	&bench_bloom_false_positive,
55562306a36Sopenharmony_ci	&bench_hashmap_without_bloom,
55662306a36Sopenharmony_ci	&bench_hashmap_with_bloom,
55762306a36Sopenharmony_ci	&bench_bpf_loop,
55862306a36Sopenharmony_ci	&bench_strncmp_no_helper,
55962306a36Sopenharmony_ci	&bench_strncmp_helper,
56062306a36Sopenharmony_ci	&bench_bpf_hashmap_full_update,
56162306a36Sopenharmony_ci	&bench_local_storage_cache_seq_get,
56262306a36Sopenharmony_ci	&bench_local_storage_cache_interleaved_get,
56362306a36Sopenharmony_ci	&bench_local_storage_cache_hashmap_control,
56462306a36Sopenharmony_ci	&bench_local_storage_tasks_trace,
56562306a36Sopenharmony_ci	&bench_bpf_hashmap_lookup,
56662306a36Sopenharmony_ci	&bench_local_storage_create,
56762306a36Sopenharmony_ci	&bench_htab_mem,
56862306a36Sopenharmony_ci};
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic void find_benchmark(void)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	int i;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (!env.bench_name) {
57562306a36Sopenharmony_ci		fprintf(stderr, "benchmark name is not specified\n");
57662306a36Sopenharmony_ci		exit(1);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(benchs); i++) {
57962306a36Sopenharmony_ci		if (strcmp(benchs[i]->name, env.bench_name) == 0) {
58062306a36Sopenharmony_ci			bench = benchs[i];
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	if (!bench) {
58562306a36Sopenharmony_ci		fprintf(stderr, "benchmark '%s' not found\n", env.bench_name);
58662306a36Sopenharmony_ci		exit(1);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic void setup_benchmark(void)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	int i, err;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (!env.quiet)
59562306a36Sopenharmony_ci		printf("Setting up benchmark '%s'...\n", bench->name);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	state.producers = calloc(env.producer_cnt, sizeof(*state.producers));
59862306a36Sopenharmony_ci	state.consumers = calloc(env.consumer_cnt, sizeof(*state.consumers));
59962306a36Sopenharmony_ci	state.results = calloc(env.duration_sec + env.warmup_sec + 2,
60062306a36Sopenharmony_ci			       sizeof(*state.results));
60162306a36Sopenharmony_ci	if (!state.producers || !state.consumers || !state.results)
60262306a36Sopenharmony_ci		exit(1);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (bench->validate)
60562306a36Sopenharmony_ci		bench->validate();
60662306a36Sopenharmony_ci	if (bench->setup)
60762306a36Sopenharmony_ci		bench->setup();
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	for (i = 0; i < env.consumer_cnt; i++) {
61062306a36Sopenharmony_ci		err = pthread_create(&state.consumers[i], NULL,
61162306a36Sopenharmony_ci				     bench->consumer_thread, (void *)(long)i);
61262306a36Sopenharmony_ci		if (err) {
61362306a36Sopenharmony_ci			fprintf(stderr, "failed to create consumer thread #%d: %d\n",
61462306a36Sopenharmony_ci				i, -err);
61562306a36Sopenharmony_ci			exit(1);
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci		if (env.affinity)
61862306a36Sopenharmony_ci			set_thread_affinity(state.consumers[i],
61962306a36Sopenharmony_ci					    next_cpu(&env.cons_cpus));
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* unless explicit producer CPU list is specified, continue after
62362306a36Sopenharmony_ci	 * last consumer CPU
62462306a36Sopenharmony_ci	 */
62562306a36Sopenharmony_ci	if (!env.prod_cpus.cpus)
62662306a36Sopenharmony_ci		env.prod_cpus.next_cpu = env.cons_cpus.next_cpu;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	for (i = 0; i < env.producer_cnt; i++) {
62962306a36Sopenharmony_ci		err = pthread_create(&state.producers[i], NULL,
63062306a36Sopenharmony_ci				     bench->producer_thread, (void *)(long)i);
63162306a36Sopenharmony_ci		if (err) {
63262306a36Sopenharmony_ci			fprintf(stderr, "failed to create producer thread #%d: %d\n",
63362306a36Sopenharmony_ci				i, -err);
63462306a36Sopenharmony_ci			exit(1);
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		if (env.affinity)
63762306a36Sopenharmony_ci			set_thread_affinity(state.producers[i],
63862306a36Sopenharmony_ci					    next_cpu(&env.prod_cpus));
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (!env.quiet)
64262306a36Sopenharmony_ci		printf("Benchmark '%s' started.\n", bench->name);
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic pthread_mutex_t bench_done_mtx = PTHREAD_MUTEX_INITIALIZER;
64662306a36Sopenharmony_cistatic pthread_cond_t bench_done = PTHREAD_COND_INITIALIZER;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void collect_measurements(long delta_ns) {
64962306a36Sopenharmony_ci	int iter = state.res_cnt++;
65062306a36Sopenharmony_ci	struct bench_res *res = &state.results[iter];
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	bench->measure(res);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (bench->report_progress)
65562306a36Sopenharmony_ci		bench->report_progress(iter, res, delta_ns);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (iter == env.duration_sec + env.warmup_sec) {
65862306a36Sopenharmony_ci		pthread_mutex_lock(&bench_done_mtx);
65962306a36Sopenharmony_ci		pthread_cond_signal(&bench_done);
66062306a36Sopenharmony_ci		pthread_mutex_unlock(&bench_done_mtx);
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ciint main(int argc, char **argv)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	env.nr_cpus = get_nprocs();
66762306a36Sopenharmony_ci	parse_cmdline_args_init(argc, argv);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (env.list) {
67062306a36Sopenharmony_ci		int i;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		printf("Available benchmarks:\n");
67362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(benchs); i++) {
67462306a36Sopenharmony_ci			printf("- %s\n", benchs[i]->name);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		return 0;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	find_benchmark();
68062306a36Sopenharmony_ci	parse_cmdline_args_final(argc, argv);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	setup_benchmark();
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	setup_timer();
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	pthread_mutex_lock(&bench_done_mtx);
68762306a36Sopenharmony_ci	pthread_cond_wait(&bench_done, &bench_done_mtx);
68862306a36Sopenharmony_ci	pthread_mutex_unlock(&bench_done_mtx);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (bench->report_final)
69162306a36Sopenharmony_ci		/* skip first sample */
69262306a36Sopenharmony_ci		bench->report_final(state.results + env.warmup_sec,
69362306a36Sopenharmony_ci				    state.res_cnt - env.warmup_sec);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
697