61ddff373f
Improve subtest selection logic when using -t/-a/-d parameters. In particular, more than one subtest can be specified or a combination of tests / subtests. -a send_signal -d send_signal/send_signal_nmi* - runs send_signal test without nmi tests -a send_signal/send_signal_nmi*,find_vma - runs two send_signal subtests and find_vma test -a 'send_signal*' -a find_vma -d send_signal/send_signal_nmi* - runs 2 send_signal test and find_vma test. Disables two send_signal nmi subtests -t send_signal -t find_vma - runs two *send_signal* tests and one *find_vma* test This will allow us to have granular control over which subtests to disable in the CI system instead of disabling whole tests. Also, add new selftest to avoid possible regression when changing prog_test test name selection logic. Signed-off-by: Mykola Lysenko <mykolal@fb.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20220409001750.529930-1-mykolal@fb.com
232 lines
4.7 KiB
C
232 lines
4.7 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
/* Copyright (C) 2019 Netronome Systems, Inc. */
|
|
/* Copyright (C) 2020 Facebook, Inc. */
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <bpf/bpf.h>
|
|
#include <bpf/libbpf.h>
|
|
#include "test_progs.h"
|
|
#include "testing_helpers.h"
|
|
|
|
int parse_num_list(const char *s, bool **num_set, int *num_set_len)
|
|
{
|
|
int i, set_len = 0, new_len, num, start = 0, end = -1;
|
|
bool *set = NULL, *tmp, parsing_end = false;
|
|
char *next;
|
|
|
|
while (s[0]) {
|
|
errno = 0;
|
|
num = strtol(s, &next, 10);
|
|
if (errno)
|
|
return -errno;
|
|
|
|
if (parsing_end)
|
|
end = num;
|
|
else
|
|
start = num;
|
|
|
|
if (!parsing_end && *next == '-') {
|
|
s = next + 1;
|
|
parsing_end = true;
|
|
continue;
|
|
} else if (*next == ',') {
|
|
parsing_end = false;
|
|
s = next + 1;
|
|
end = num;
|
|
} else if (*next == '\0') {
|
|
parsing_end = false;
|
|
s = next;
|
|
end = num;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (start > end)
|
|
return -EINVAL;
|
|
|
|
if (end + 1 > set_len) {
|
|
new_len = end + 1;
|
|
tmp = realloc(set, new_len);
|
|
if (!tmp) {
|
|
free(set);
|
|
return -ENOMEM;
|
|
}
|
|
for (i = set_len; i < start; i++)
|
|
tmp[i] = false;
|
|
set = tmp;
|
|
set_len = new_len;
|
|
}
|
|
for (i = start; i <= end; i++)
|
|
set[i] = true;
|
|
}
|
|
|
|
if (!set || parsing_end)
|
|
return -EINVAL;
|
|
|
|
*num_set = set;
|
|
*num_set_len = set_len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_test_list(const char *s,
|
|
struct test_filter_set *set,
|
|
bool is_glob_pattern)
|
|
{
|
|
char *input, *state = NULL, *next;
|
|
struct test_filter *tmp, *tests = NULL;
|
|
int i, j, cnt = 0;
|
|
|
|
input = strdup(s);
|
|
if (!input)
|
|
return -ENOMEM;
|
|
|
|
while ((next = strtok_r(state ? NULL : input, ",", &state))) {
|
|
char *subtest_str = strchr(next, '/');
|
|
char *pattern = NULL;
|
|
int glob_chars = 0;
|
|
|
|
tmp = realloc(tests, sizeof(*tests) * (cnt + 1));
|
|
if (!tmp)
|
|
goto err;
|
|
tests = tmp;
|
|
|
|
tests[cnt].subtest_cnt = 0;
|
|
tests[cnt].subtests = NULL;
|
|
|
|
if (is_glob_pattern) {
|
|
pattern = "%s";
|
|
} else {
|
|
pattern = "*%s*";
|
|
glob_chars = 2;
|
|
}
|
|
|
|
if (subtest_str) {
|
|
char **tmp_subtests = NULL;
|
|
int subtest_cnt = tests[cnt].subtest_cnt;
|
|
|
|
*subtest_str = '\0';
|
|
subtest_str += 1;
|
|
tmp_subtests = realloc(tests[cnt].subtests,
|
|
sizeof(*tmp_subtests) *
|
|
(subtest_cnt + 1));
|
|
if (!tmp_subtests)
|
|
goto err;
|
|
tests[cnt].subtests = tmp_subtests;
|
|
|
|
tests[cnt].subtests[subtest_cnt] =
|
|
malloc(strlen(subtest_str) + glob_chars + 1);
|
|
if (!tests[cnt].subtests[subtest_cnt])
|
|
goto err;
|
|
sprintf(tests[cnt].subtests[subtest_cnt],
|
|
pattern,
|
|
subtest_str);
|
|
|
|
tests[cnt].subtest_cnt++;
|
|
}
|
|
|
|
tests[cnt].name = malloc(strlen(next) + glob_chars + 1);
|
|
if (!tests[cnt].name)
|
|
goto err;
|
|
sprintf(tests[cnt].name, pattern, next);
|
|
|
|
cnt++;
|
|
}
|
|
|
|
tmp = realloc(set->tests, sizeof(*tests) * (cnt + set->cnt));
|
|
if (!tmp)
|
|
goto err;
|
|
|
|
memcpy(tmp + set->cnt, tests, sizeof(*tests) * cnt);
|
|
set->tests = tmp;
|
|
set->cnt += cnt;
|
|
|
|
free(tests);
|
|
free(input);
|
|
return 0;
|
|
|
|
err:
|
|
for (i = 0; i < cnt; i++) {
|
|
for (j = 0; j < tests[i].subtest_cnt; j++)
|
|
free(tests[i].subtests[j]);
|
|
|
|
free(tests[i].name);
|
|
}
|
|
free(tests);
|
|
free(input);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
__u32 link_info_prog_id(const struct bpf_link *link, struct bpf_link_info *info)
|
|
{
|
|
__u32 info_len = sizeof(*info);
|
|
int err;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
err = bpf_obj_get_info_by_fd(bpf_link__fd(link), info, &info_len);
|
|
if (err) {
|
|
printf("failed to get link info: %d\n", -errno);
|
|
return 0;
|
|
}
|
|
return info->prog_id;
|
|
}
|
|
|
|
int extra_prog_load_log_flags = 0;
|
|
|
|
int bpf_prog_test_load(const char *file, enum bpf_prog_type type,
|
|
struct bpf_object **pobj, int *prog_fd)
|
|
{
|
|
LIBBPF_OPTS(bpf_object_open_opts, opts,
|
|
.kernel_log_level = extra_prog_load_log_flags,
|
|
);
|
|
struct bpf_object *obj;
|
|
struct bpf_program *prog;
|
|
__u32 flags;
|
|
int err;
|
|
|
|
obj = bpf_object__open_file(file, &opts);
|
|
if (!obj)
|
|
return -errno;
|
|
|
|
prog = bpf_object__next_program(obj, NULL);
|
|
if (!prog) {
|
|
err = -ENOENT;
|
|
goto err_out;
|
|
}
|
|
|
|
if (type != BPF_PROG_TYPE_UNSPEC)
|
|
bpf_program__set_type(prog, type);
|
|
|
|
flags = bpf_program__flags(prog) | BPF_F_TEST_RND_HI32;
|
|
bpf_program__set_flags(prog, flags);
|
|
|
|
err = bpf_object__load(obj);
|
|
if (err)
|
|
goto err_out;
|
|
|
|
*pobj = obj;
|
|
*prog_fd = bpf_program__fd(prog);
|
|
|
|
return 0;
|
|
err_out:
|
|
bpf_object__close(obj);
|
|
return err;
|
|
}
|
|
|
|
int bpf_test_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|
size_t insns_cnt, const char *license,
|
|
__u32 kern_version, char *log_buf,
|
|
size_t log_buf_sz)
|
|
{
|
|
LIBBPF_OPTS(bpf_prog_load_opts, opts,
|
|
.kern_version = kern_version,
|
|
.prog_flags = BPF_F_TEST_RND_HI32,
|
|
.log_level = extra_prog_load_log_flags,
|
|
.log_buf = log_buf,
|
|
.log_size = log_buf_sz,
|
|
);
|
|
|
|
return bpf_prog_load(type, NULL, license, insns, insns_cnt, &opts);
|
|
}
|