| // SPDX-License-Identifier: GPL-2.0 | 
 | #include <stdio.h> | 
 | #include <bpf/libbpf.h> | 
 | #include <util/llvm-utils.h> | 
 | #include <util/cache.h> | 
 | #include "llvm.h" | 
 | #include "tests.h" | 
 | #include "debug.h" | 
 | #include "util.h" | 
 |  | 
 | #ifdef HAVE_LIBBPF_SUPPORT | 
 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) | 
 | { | 
 | 	struct bpf_object *obj; | 
 |  | 
 | 	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); | 
 | 	if (libbpf_get_error(obj)) | 
 | 		return TEST_FAIL; | 
 | 	bpf_object__close(obj); | 
 | 	return TEST_OK; | 
 | } | 
 | #else | 
 | static int test__bpf_parsing(void *obj_buf __maybe_unused, | 
 | 			     size_t obj_buf_sz __maybe_unused) | 
 | { | 
 | 	pr_debug("Skip bpf parsing\n"); | 
 | 	return TEST_OK; | 
 | } | 
 | #endif | 
 |  | 
 | static struct { | 
 | 	const char *source; | 
 | 	const char *desc; | 
 | 	bool should_load_fail; | 
 | } bpf_source_table[__LLVM_TESTCASE_MAX] = { | 
 | 	[LLVM_TESTCASE_BASE] = { | 
 | 		.source = test_llvm__bpf_base_prog, | 
 | 		.desc = "Basic BPF llvm compile", | 
 | 	}, | 
 | 	[LLVM_TESTCASE_KBUILD] = { | 
 | 		.source = test_llvm__bpf_test_kbuild_prog, | 
 | 		.desc = "kbuild searching", | 
 | 	}, | 
 | 	[LLVM_TESTCASE_BPF_PROLOGUE] = { | 
 | 		.source = test_llvm__bpf_test_prologue_prog, | 
 | 		.desc = "Compile source for BPF prologue generation", | 
 | 	}, | 
 | 	[LLVM_TESTCASE_BPF_RELOCATION] = { | 
 | 		.source = test_llvm__bpf_test_relocation, | 
 | 		.desc = "Compile source for BPF relocation", | 
 | 		.should_load_fail = true, | 
 | 	}, | 
 | }; | 
 |  | 
 | int | 
 | test_llvm__fetch_bpf_obj(void **p_obj_buf, | 
 | 			 size_t *p_obj_buf_sz, | 
 | 			 enum test_llvm__testcase idx, | 
 | 			 bool force, | 
 | 			 bool *should_load_fail) | 
 | { | 
 | 	const char *source; | 
 | 	const char *desc; | 
 | 	const char *tmpl_old, *clang_opt_old; | 
 | 	char *tmpl_new = NULL, *clang_opt_new = NULL; | 
 | 	int err, old_verbose, ret = TEST_FAIL; | 
 |  | 
 | 	if (idx >= __LLVM_TESTCASE_MAX) | 
 | 		return TEST_FAIL; | 
 |  | 
 | 	source = bpf_source_table[idx].source; | 
 | 	desc = bpf_source_table[idx].desc; | 
 | 	if (should_load_fail) | 
 | 		*should_load_fail = bpf_source_table[idx].should_load_fail; | 
 |  | 
 | 	/* | 
 | 	 * Skip this test if user's .perfconfig doesn't set [llvm] section | 
 | 	 * and clang is not found in $PATH, and this is not perf test -v | 
 | 	 */ | 
 | 	if (!force && (verbose <= 0 && | 
 | 		       !llvm_param.user_set_param && | 
 | 		       llvm__search_clang())) { | 
 | 		pr_debug("No clang and no verbosive, skip this test\n"); | 
 | 		return TEST_SKIP; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * llvm is verbosity when error. Suppress all error output if | 
 | 	 * not 'perf test -v'. | 
 | 	 */ | 
 | 	old_verbose = verbose; | 
 | 	if (verbose == 0) | 
 | 		verbose = -1; | 
 |  | 
 | 	*p_obj_buf = NULL; | 
 | 	*p_obj_buf_sz = 0; | 
 |  | 
 | 	if (!llvm_param.clang_bpf_cmd_template) | 
 | 		goto out; | 
 |  | 
 | 	if (!llvm_param.clang_opt) | 
 | 		llvm_param.clang_opt = strdup(""); | 
 |  | 
 | 	err = asprintf(&tmpl_new, "echo '%s' | %s%s", source, | 
 | 		       llvm_param.clang_bpf_cmd_template, | 
 | 		       old_verbose ? "" : " 2>/dev/null"); | 
 | 	if (err < 0) | 
 | 		goto out; | 
 | 	err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); | 
 | 	if (err < 0) | 
 | 		goto out; | 
 |  | 
 | 	tmpl_old = llvm_param.clang_bpf_cmd_template; | 
 | 	llvm_param.clang_bpf_cmd_template = tmpl_new; | 
 | 	clang_opt_old = llvm_param.clang_opt; | 
 | 	llvm_param.clang_opt = clang_opt_new; | 
 |  | 
 | 	err = llvm__compile_bpf("-", p_obj_buf, p_obj_buf_sz); | 
 |  | 
 | 	llvm_param.clang_bpf_cmd_template = tmpl_old; | 
 | 	llvm_param.clang_opt = clang_opt_old; | 
 |  | 
 | 	verbose = old_verbose; | 
 | 	if (err) | 
 | 		goto out; | 
 |  | 
 | 	ret = TEST_OK; | 
 | out: | 
 | 	free(tmpl_new); | 
 | 	free(clang_opt_new); | 
 | 	if (ret != TEST_OK) | 
 | 		pr_debug("Failed to compile test case: '%s'\n", desc); | 
 | 	return ret; | 
 | } | 
 |  | 
 | int test__llvm(struct test *test __maybe_unused, int subtest) | 
 | { | 
 | 	int ret; | 
 | 	void *obj_buf = NULL; | 
 | 	size_t obj_buf_sz = 0; | 
 | 	bool should_load_fail = false; | 
 |  | 
 | 	if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) | 
 | 		return TEST_FAIL; | 
 |  | 
 | 	ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | 
 | 				       subtest, false, &should_load_fail); | 
 |  | 
 | 	if (ret == TEST_OK && !should_load_fail) { | 
 | 		ret = test__bpf_parsing(obj_buf, obj_buf_sz); | 
 | 		if (ret != TEST_OK) { | 
 | 			pr_debug("Failed to parse test case '%s'\n", | 
 | 				 bpf_source_table[subtest].desc); | 
 | 		} | 
 | 	} | 
 | 	free(obj_buf); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | int test__llvm_subtest_get_nr(void) | 
 | { | 
 | 	return __LLVM_TESTCASE_MAX; | 
 | } | 
 |  | 
 | const char *test__llvm_subtest_get_desc(int subtest) | 
 | { | 
 | 	if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) | 
 | 		return NULL; | 
 |  | 
 | 	return bpf_source_table[subtest].desc; | 
 | } |