root/tools/testing/selftests/powerpc/pmu/per_event_excludes.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2014, Michael Ellerman, IBM Corp.
 */

#define _GNU_SOURCE

#include <elf.h>
#include <limits.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>

#include "event.h"
#include "lib.h"
#include "utils.h"

/*
 * Test that per-event excludes work.
 */

static int per_event_excludes(void)
{
        struct event *e, events[4];
        int i;

        SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));

        /*
         * We need to create the events disabled, otherwise the running/enabled
         * counts don't match up.
         */
        e = &events[0];
        event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
                        PERF_TYPE_HARDWARE, "instructions");
        e->attr.disabled = 1;

        e = &events[1];
        event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
                        PERF_TYPE_HARDWARE, "instructions(k)");
        e->attr.disabled = 1;
        e->attr.exclude_user = 1;
        e->attr.exclude_hv = 1;

        e = &events[2];
        event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
                        PERF_TYPE_HARDWARE, "instructions(h)");
        e->attr.disabled = 1;
        e->attr.exclude_user = 1;
        e->attr.exclude_kernel = 1;

        e = &events[3];
        event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS,
                        PERF_TYPE_HARDWARE, "instructions(u)");
        e->attr.disabled = 1;
        e->attr.exclude_hv = 1;
        e->attr.exclude_kernel = 1;

        FAIL_IF(event_open(&events[0]));

        /*
         * The open here will fail if we don't have per event exclude support,
         * because the second event has an incompatible set of exclude settings
         * and we're asking for the events to be in a group.
         */
        for (i = 1; i < 4; i++)
                FAIL_IF(event_open_with_group(&events[i], events[0].fd));

        /*
         * Even though the above will fail without per-event excludes we keep
         * testing in order to be thorough.
         */
        prctl(PR_TASK_PERF_EVENTS_ENABLE);

        /* Spin for a while */
        for (i = 0; i < INT_MAX; i++)
                asm volatile("" : : : "memory");

        prctl(PR_TASK_PERF_EVENTS_DISABLE);

        for (i = 0; i < 4; i++) {
                FAIL_IF(event_read(&events[i]));
                event_report(&events[i]);
        }

        /*
         * We should see that all events have enabled == running. That
         * shows that they were all on the PMU at once.
         */
        for (i = 0; i < 4; i++)
                FAIL_IF(events[i].result.running != events[i].result.enabled);

        /*
         * We can also check that the result for instructions is >= all the
         * other counts. That's because it is counting all instructions while
         * the others are counting a subset.
         */
        for (i = 1; i < 4; i++)
                FAIL_IF(events[0].result.value < events[i].result.value);

        for (i = 0; i < 4; i++)
                event_close(&events[i]);

        return 0;
}

int main(void)
{
        return test_harness(per_event_excludes, "per_event_excludes");
}