To parse command line the bench utility uses the argp_parse() function. This function takes as an argument a parent 'struct argp' structure which defines common command line options and an array of children 'struct argp' structures which defines additional command line options for particular benchmarks. This implementation doesn't allow benchmarks to share option names, e.g., if two benchmarks want to use, say, the --option option, then only one of them will succeed (the first one encountered in the array). This will be convenient if same option names could be used in different benchmarks (with the same semantics, e.g., --nr_loops=N). Fix this by calling the argp_parse() function twice. The first call is the same as it was before, with all children argps, and helps to find the benchmark name and to print a combined help message if anything is wrong. Given the name, we can call the argp_parse the second time, but now the children array points only to a correct benchmark thus always calling the correct parsers. (If there's no a specific list of arguments, then only one call to argp_parse will be done.) Signed-off-by: Anton Protopopov <aspsk@isovalent.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20230213091519.1202813-4-aspsk@isovalent.com
164 lines
3.5 KiB
C
164 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2021. Huawei Technologies Co., Ltd */
|
|
#include <argp.h>
|
|
#include "bench.h"
|
|
#include "strncmp_bench.skel.h"
|
|
|
|
static struct strncmp_ctx {
|
|
struct strncmp_bench *skel;
|
|
} ctx;
|
|
|
|
static struct strncmp_args {
|
|
u32 cmp_str_len;
|
|
} args = {
|
|
.cmp_str_len = 32,
|
|
};
|
|
|
|
enum {
|
|
ARG_CMP_STR_LEN = 5000,
|
|
};
|
|
|
|
static const struct argp_option opts[] = {
|
|
{ "cmp-str-len", ARG_CMP_STR_LEN, "CMP_STR_LEN", 0,
|
|
"Set the length of compared string" },
|
|
{},
|
|
};
|
|
|
|
static error_t strncmp_parse_arg(int key, char *arg, struct argp_state *state)
|
|
{
|
|
switch (key) {
|
|
case ARG_CMP_STR_LEN:
|
|
args.cmp_str_len = strtoul(arg, NULL, 10);
|
|
if (!args.cmp_str_len ||
|
|
args.cmp_str_len >= sizeof(ctx.skel->bss->str)) {
|
|
fprintf(stderr, "Invalid cmp str len (limit %zu)\n",
|
|
sizeof(ctx.skel->bss->str));
|
|
argp_usage(state);
|
|
}
|
|
break;
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct argp bench_strncmp_argp = {
|
|
.options = opts,
|
|
.parser = strncmp_parse_arg,
|
|
};
|
|
|
|
static void strncmp_validate(void)
|
|
{
|
|
if (env.consumer_cnt != 1) {
|
|
fprintf(stderr, "strncmp benchmark doesn't support multi-consumer!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void strncmp_setup(void)
|
|
{
|
|
int err;
|
|
char *target;
|
|
size_t i, sz;
|
|
|
|
sz = sizeof(ctx.skel->rodata->target);
|
|
if (!sz || sz < sizeof(ctx.skel->bss->str)) {
|
|
fprintf(stderr, "invalid string size (target %zu, src %zu)\n",
|
|
sz, sizeof(ctx.skel->bss->str));
|
|
exit(1);
|
|
}
|
|
|
|
setup_libbpf();
|
|
|
|
ctx.skel = strncmp_bench__open();
|
|
if (!ctx.skel) {
|
|
fprintf(stderr, "failed to open skeleton\n");
|
|
exit(1);
|
|
}
|
|
|
|
srandom(time(NULL));
|
|
target = ctx.skel->rodata->target;
|
|
for (i = 0; i < sz - 1; i++)
|
|
target[i] = '1' + random() % 9;
|
|
target[sz - 1] = '\0';
|
|
|
|
ctx.skel->rodata->cmp_str_len = args.cmp_str_len;
|
|
|
|
memcpy(ctx.skel->bss->str, target, args.cmp_str_len);
|
|
ctx.skel->bss->str[args.cmp_str_len] = '\0';
|
|
/* Make bss->str < rodata->target */
|
|
ctx.skel->bss->str[args.cmp_str_len - 1] -= 1;
|
|
|
|
err = strncmp_bench__load(ctx.skel);
|
|
if (err) {
|
|
fprintf(stderr, "failed to load skeleton\n");
|
|
strncmp_bench__destroy(ctx.skel);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void strncmp_attach_prog(struct bpf_program *prog)
|
|
{
|
|
struct bpf_link *link;
|
|
|
|
link = bpf_program__attach(prog);
|
|
if (!link) {
|
|
fprintf(stderr, "failed to attach program!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void strncmp_no_helper_setup(void)
|
|
{
|
|
strncmp_setup();
|
|
strncmp_attach_prog(ctx.skel->progs.strncmp_no_helper);
|
|
}
|
|
|
|
static void strncmp_helper_setup(void)
|
|
{
|
|
strncmp_setup();
|
|
strncmp_attach_prog(ctx.skel->progs.strncmp_helper);
|
|
}
|
|
|
|
static void *strncmp_producer(void *ctx)
|
|
{
|
|
while (true)
|
|
(void)syscall(__NR_getpgid);
|
|
return NULL;
|
|
}
|
|
|
|
static void *strncmp_consumer(void *ctx)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static void strncmp_measure(struct bench_res *res)
|
|
{
|
|
res->hits = atomic_swap(&ctx.skel->bss->hits, 0);
|
|
}
|
|
|
|
const struct bench bench_strncmp_no_helper = {
|
|
.name = "strncmp-no-helper",
|
|
.argp = &bench_strncmp_argp,
|
|
.validate = strncmp_validate,
|
|
.setup = strncmp_no_helper_setup,
|
|
.producer_thread = strncmp_producer,
|
|
.consumer_thread = strncmp_consumer,
|
|
.measure = strncmp_measure,
|
|
.report_progress = hits_drops_report_progress,
|
|
.report_final = hits_drops_report_final,
|
|
};
|
|
|
|
const struct bench bench_strncmp_helper = {
|
|
.name = "strncmp-helper",
|
|
.argp = &bench_strncmp_argp,
|
|
.validate = strncmp_validate,
|
|
.setup = strncmp_helper_setup,
|
|
.producer_thread = strncmp_producer,
|
|
.consumer_thread = strncmp_consumer,
|
|
.measure = strncmp_measure,
|
|
.report_progress = hits_drops_report_progress,
|
|
.report_final = hits_drops_report_final,
|
|
};
|