// 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; 
 | 
} 
 |