#include <test_progs.h>
#include <unistd.h>
#include <libarena/common.h>
#include <libarena/asan.h>
#include <libarena/buddy.h>
#include <libarena/userspace.h>
#include "libarena/libarena.skel.h"
static void run_libarena_test(struct libarena *skel, struct bpf_program *prog,
const char *name)
{
int ret;
if (!strstr(name, "test_buddy")) {
ret = libarena_run_prog(bpf_program__fd(skel->progs.arena_buddy_reset));
if (!ASSERT_OK(ret, "arena_buddy_reset"))
return;
}
ret = libarena_run_prog(bpf_program__fd(prog));
ASSERT_OK(ret, name);
}
static void *run_libarena_parallel_prog(void *arg)
{
struct bpf_program *prog = arg;
return (void *)(long)libarena_run_prog(bpf_program__fd(prog));
}
#define MAX_PARTEST_SUFFIX (12)
#define MAX_PARTEST_NAME (1024)
#define MAX_PARTEST_PREFIX (MAX_PARTEST_NAME - MAX_PARTEST_SUFFIX)
static int run_libarena_parallel_fini(struct libarena *skel, const char *name,
size_t prefixlen)
{
char tdname[MAX_PARTEST_NAME];
struct bpf_program *fini_prog;
int ret;
ret = snprintf(tdname, sizeof(tdname), "%.*s__fini", (int)prefixlen, name);
if (!ASSERT_LT(ret, sizeof(tdname), "partest fini name"))
return -ENAMETOOLONG;
fini_prog = bpf_object__find_program_by_name(skel->obj, tdname);
if (!ASSERT_TRUE(fini_prog, "partest fini prog"))
return -ENOENT;
ret = libarena_run_prog(bpf_program__fd(fini_prog));
ASSERT_OK(ret, tdname);
return ret;
}
static int run_libarena_parallel_test_workers(struct libarena *skel,
const char *name, size_t prefixlen)
{
pthread_t *threads = NULL, *tmp_threads;
char tdname[MAX_PARTEST_NAME];
struct bpf_program *tdprog;
uint32_t nthreads;
void *thread_ret;
int ret, err = 0;
int i;
for (nthreads = 0; nthreads < UINT_MAX; nthreads++) {
ret = snprintf(tdname, sizeof(tdname), "%.*s__%u", (int)prefixlen,
name, nthreads);
if (!ASSERT_LT(ret, sizeof(tdname), "test worker name")) {
err = -ENAMETOOLONG;
break;
}
tdprog = bpf_object__find_program_by_name(skel->obj, tdname);
if (!tdprog)
break;
tmp_threads = realloc(threads, (nthreads + 1) * sizeof(*threads));
if (!ASSERT_TRUE(tmp_threads, "realloc")) {
err = -ENOMEM;
break;
}
threads = tmp_threads;
ret = pthread_create(&threads[nthreads], NULL,
run_libarena_parallel_prog,
tdprog);
if (!ASSERT_OK(ret, "pthread_create")) {
err = ret;
break;
}
}
for (i = 0; i < nthreads; i++) {
ret = pthread_join(threads[i], &thread_ret);
if (!ASSERT_OK(ret, "pthread_join")) {
err = err ?: ret;
continue;
}
err = err ?: (long)thread_ret;
}
free(threads);
return err;
}
static bool libarena_parallel_test_enabled(struct libarena *skel,
const char *prefix,
size_t prefixlen)
{
struct bpf_program *prog;
char progname[MAX_PARTEST_NAME];
int ret;
ret = snprintf(progname, sizeof(progname), "%.*s__enabled", (int)prefixlen,
prefix);
if (!ASSERT_LT(ret, sizeof(progname), "partest enabled name"))
return false;
prog = bpf_object__find_program_by_name(skel->obj, progname);
if (!prog)
return true;
ret = libarena_run_prog(bpf_program__fd(prog));
if (ret == -EOPNOTSUPP)
return false;
if (!ASSERT_OK(ret, progname))
return false;
return true;
}
static void run_libarena_parallel_test(struct libarena *skel, struct bpf_program *prog,
const char *name)
{
char testname[MAX_PARTEST_NAME];
size_t prefixlen;
const char *pos;
int ret;
pos = strstr(name, "__init");
if (!pos)
return;
prefixlen = pos - name;
if (!ASSERT_LT(prefixlen, MAX_PARTEST_PREFIX, "partest prefix too long"))
return;
ret = snprintf(testname, sizeof(testname), "%.*s", (int)prefixlen, name);
if (!ASSERT_LT(ret, sizeof(testname), "partest test name"))
return;
if (!test__start_subtest(testname))
return;
if (!libarena_parallel_test_enabled(skel, testname, prefixlen)) {
test__skip();
return;
}
ret = libarena_run_prog(bpf_program__fd(skel->progs.arena_buddy_reset));
if (!ASSERT_OK(ret, "arena_buddy_reset"))
return;
ret = libarena_run_prog(bpf_program__fd(prog));
if (!ASSERT_OK(ret, testname))
return;
ret = run_libarena_parallel_test_workers(skel, name, prefixlen);
ASSERT_OK(ret, testname);
run_libarena_parallel_fini(skel, name, prefixlen);
}
void test_libarena(void)
{
struct arena_alloc_reserve_args args;
struct libarena *skel;
struct bpf_program *prog;
int ret;
skel = libarena__open_and_load();
if (!ASSERT_OK_PTR(skel, "open_and_load"))
return;
ret = libarena__attach(skel);
if (!ASSERT_OK(ret, "attach"))
goto out;
args.nr_pages = ARENA_RESERVE_PAGES_DFL;
ret = libarena_run_prog_args(bpf_program__fd(skel->progs.arena_alloc_reserve),
&args, sizeof(args));
if (!ASSERT_OK(ret, "arena_alloc_reserve"))
goto out;
bpf_object__for_each_program(prog, skel->obj) {
const char *name = bpf_program__name(prog);
if (libarena_is_parallel_test_prog(name)) {
run_libarena_parallel_test(skel, prog, name);
continue;
}
if (!libarena_is_test_prog(name))
continue;
if (!test__start_subtest(name))
continue;
run_libarena_test(skel, prog, name);
}
out:
libarena__destroy(skel);
}