root/tools/perf/arch/x86/util/evlist.c
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "../../../util/evlist.h"
#include "../../../util/evsel.h"
#include "topdown.h"
#include "evsel.h"

int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
{
        /*
         * Currently the following topdown events sequence are supported to
         * move and regroup correctly.
         *
         * a. all events in a group
         *    perf stat -e "{instructions,topdown-retiring,slots}" -C0 sleep 1
         *    WARNING: events were regrouped to match PMUs
         *     Performance counter stats for 'CPU(s) 0':
         *          15,066,240     slots
         *          1,899,760      instructions
         *          2,126,998      topdown-retiring
         * b. all events not in a group
         *    perf stat -e "instructions,topdown-retiring,slots" -C0 sleep 1
         *    WARNING: events were regrouped to match PMUs
         *     Performance counter stats for 'CPU(s) 0':
         *          2,045,561      instructions
         *          17,108,370     slots
         *          2,281,116      topdown-retiring
         * c. slots event in a group but topdown metrics events outside the group
         *    perf stat -e "{instructions,slots},topdown-retiring" -C0 sleep 1
         *    WARNING: events were regrouped to match PMUs
         *     Performance counter stats for 'CPU(s) 0':
         *         20,323,878      slots
         *          2,634,884      instructions
         *          3,028,656      topdown-retiring
         * d. slots event and topdown metrics events in two groups
         *    perf stat -e "{instructions,slots},{topdown-retiring}" -C0 sleep 1
         *    WARNING: events were regrouped to match PMUs
         *     Performance counter stats for 'CPU(s) 0':
         *         26,319,024      slots
         *          2,427,791      instructions
         *          2,683,508      topdown-retiring
         * e. slots event and metrics event are not in a group and not adjacent
         *    perf stat -e "{instructions,slots},cycles,topdown-retiring" -C0 sleep 1
         *    WARNING: events were regrouped to match PMUs
         *         68,433,522      slots
         *          8,856,102      topdown-retiring
         *          7,791,494      instructions
         *         11,469,513      cycles
         */
        if (topdown_sys_has_perf_metrics() &&
            (arch_evsel__must_be_in_group(lhs) || arch_evsel__must_be_in_group(rhs))) {
                /* Ensure the topdown slots comes first. */
                if (arch_is_topdown_slots(lhs))
                        return -1;
                if (arch_is_topdown_slots(rhs))
                        return 1;

                /*
                 * Move topdown metrics events forward only when topdown metrics
                 * events are not in same group with previous slots event. If
                 * topdown metrics events are already in same group with slots
                 * event, do nothing.
                 */
                if (lhs->core.leader != rhs->core.leader) {
                        bool lhs_topdown = arch_is_topdown_metrics(lhs);
                        bool rhs_topdown = arch_is_topdown_metrics(rhs);

                        if (lhs_topdown && !rhs_topdown)
                                return -1;
                        if (!lhs_topdown && rhs_topdown)
                                return 1;
                }
        }

        /* Retire latency event should not be group leader*/
        if (lhs->retire_lat && !rhs->retire_lat)
                return 1;
        if (!lhs->retire_lat && rhs->retire_lat)
                return -1;

        /* Default ordering by insertion index. */
        return lhs->core.idx - rhs->core.idx;
}

int arch_evlist__add_required_events(struct list_head *list)
{
        struct evsel *pos, *metric_event = NULL;
        int idx = 0;

        if (!topdown_sys_has_perf_metrics())
                return 0;

        list_for_each_entry(pos, list, core.node) {
                if (arch_is_topdown_slots(pos)) {
                        /* Slots event already present, nothing to do. */
                        return 0;
                }
                if (metric_event == NULL && arch_is_topdown_metrics(pos))
                        metric_event = pos;
                idx++;
        }
        if (metric_event == NULL) {
                /* No topdown metric events, nothing to do. */
                return 0;
        }
        return topdown_insert_slots_event(list, idx + 1, metric_event);
}