root/tools/testing/selftests/bpf/prog_tests/get_branch_snapshot.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2021 Facebook */
#include <test_progs.h>
#include "get_branch_snapshot.skel.h"

static int *pfd_array;
static int cpu_cnt;

static bool is_hypervisor(void)
{
        char *line = NULL;
        bool ret = false;
        size_t len;
        FILE *fp;

        fp = fopen("/proc/cpuinfo", "r");
        if (!fp)
                return false;

        while (getline(&line, &len, fp) != -1) {
                if (!strncmp(line, "flags", 5)) {
                        if (strstr(line, "hypervisor") != NULL)
                                ret = true;
                        break;
                }
        }

        free(line);
        fclose(fp);
        return ret;
}

static int create_perf_events(void)
{
        struct perf_event_attr attr = {0};
        int cpu;

        /* create perf event */
        attr.size = sizeof(attr);
        attr.type = PERF_TYPE_HARDWARE;
        attr.config = PERF_COUNT_HW_CPU_CYCLES;
        attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
        attr.branch_sample_type = PERF_SAMPLE_BRANCH_KERNEL |
                PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY;

        cpu_cnt = libbpf_num_possible_cpus();
        pfd_array = malloc(sizeof(int) * cpu_cnt);
        if (!pfd_array) {
                cpu_cnt = 0;
                return 1;
        }

        for (cpu = 0; cpu < cpu_cnt; cpu++) {
                pfd_array[cpu] = syscall(__NR_perf_event_open, &attr,
                                         -1, cpu, -1, PERF_FLAG_FD_CLOEXEC);
                if (pfd_array[cpu] < 0)
                        break;
        }

        return cpu == 0;
}

static void close_perf_events(void)
{
        int cpu, fd;

        for (cpu = 0; cpu < cpu_cnt; cpu++) {
                fd = pfd_array[cpu];
                if (fd < 0)
                        break;
                close(fd);
        }
        free(pfd_array);
}

void serial_test_get_branch_snapshot(void)
{
        struct get_branch_snapshot *skel = NULL;
        int err;

        /* Skip the test before we fix LBR snapshot for hypervisor. */
        if (is_hypervisor()) {
                test__skip();
                return;
        }

        if (create_perf_events()) {
                test__skip();  /* system doesn't support LBR */
                goto cleanup;
        }

        skel = get_branch_snapshot__open_and_load();
        if (!ASSERT_OK_PTR(skel, "get_branch_snapshot__open_and_load"))
                goto cleanup;

        err = kallsyms_find("bpf_testmod_loop_test", &skel->bss->address_low);
        if (!ASSERT_OK(err, "kallsyms_find"))
                goto cleanup;

        /* Just a guess for the end of this function, as module functions
         * in /proc/kallsyms could come in any order.
         */
        skel->bss->address_high = skel->bss->address_low + 128;

        err = get_branch_snapshot__attach(skel);
        if (!ASSERT_OK(err, "get_branch_snapshot__attach"))
                goto cleanup;

        trigger_module_test_read(100);

        if (skel->bss->total_entries < 16) {
                /* too few entries for the hit/waste test */
                test__skip();
                goto cleanup;
        }

        ASSERT_GT(skel->bss->test1_hits, 6, "find_looptest_in_lbr");

        /* Given we stop LBR in software, we will waste a few entries.
         * But we should try to waste as few as possible entries. We are at
         * about 7 on x86_64 systems.
         * Add a check for < 10 so that we get heads-up when something
         * changes and wastes too many entries.
         */
        ASSERT_LT(skel->bss->wasted_entries, 10, "check_wasted_entries");

cleanup:
        get_branch_snapshot__destroy(skel);
        close_perf_events();
}