+__weak int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
+{
+ /* Order by insertion index. */
+ return lhs->core.idx - rhs->core.idx;
+}
+
+static int evlist__cmp(void *state, const struct list_head *l, const struct list_head *r)
+{
+ const struct perf_evsel *lhs_core = container_of(l, struct perf_evsel, node);
+ const struct evsel *lhs = container_of(lhs_core, struct evsel, core);
+ const struct perf_evsel *rhs_core = container_of(r, struct perf_evsel, node);
+ const struct evsel *rhs = container_of(rhs_core, struct evsel, core);
+ int *leader_idx = state;
+ int lhs_leader_idx = *leader_idx, rhs_leader_idx = *leader_idx, ret;
+ const char *lhs_pmu_name, *rhs_pmu_name;
+
+ /*
+ * First sort by grouping/leader. Read the leader idx only if the evsel
+ * is part of a group, as -1 indicates no group.
+ */
+ if (lhs_core->leader != lhs_core || lhs_core->nr_members > 1)
+ lhs_leader_idx = lhs_core->leader->idx;
+ if (rhs_core->leader != rhs_core || rhs_core->nr_members > 1)
+ rhs_leader_idx = rhs_core->leader->idx;
+
+ if (lhs_leader_idx != rhs_leader_idx)
+ return lhs_leader_idx - rhs_leader_idx;
+
+ /* Group by PMU. Groups can't span PMUs. */
+ lhs_pmu_name = evsel__group_pmu_name(lhs);
+ rhs_pmu_name = evsel__group_pmu_name(rhs);
+ ret = strcmp(lhs_pmu_name, rhs_pmu_name);
+ if (ret)
+ return ret;
+
+ /* Architecture specific sorting. */
+ return arch_evlist__cmp(lhs, rhs);
+}
+
+static void parse_events__sort_events_and_fix_groups(struct list_head *list)
+{
+ int idx = -1;
+ struct evsel *pos, *cur_leader = NULL;
+ struct perf_evsel *cur_leaders_grp = NULL;
+
+ /*
+ * Compute index to insert ungrouped events at. Place them where the
+ * first ungrouped event appears.
+ */
+ list_for_each_entry(pos, list, core.node) {
+ const struct evsel *pos_leader = evsel__leader(pos);
+
+ if (pos != pos_leader || pos->core.nr_members > 1)
+ continue;
+
+ idx = pos->core.idx;
+ break;
+ }
+
+ /* Sort events. */
+ list_sort(&idx, list, evlist__cmp);
+
+ /*
+ * Recompute groups, splitting for PMUs and adding groups for events
+ * that require them.
+ */
+ idx = 0;
+ list_for_each_entry(pos, list, core.node) {
+ const struct evsel *pos_leader = evsel__leader(pos);
+ const char *pos_pmu_name = evsel__group_pmu_name(pos);
+ const char *cur_leader_pmu_name, *pos_leader_pmu_name;
+ bool force_grouped = arch_evsel__must_be_in_group(pos);
+
+ /* Reset index and nr_members. */
+ pos->core.idx = idx++;
+ pos->core.nr_members = 0;
+
+ /*
+ * Set the group leader respecting the given groupings and that
+ * groups can't span PMUs.
+ */
+ if (!cur_leader)
+ cur_leader = pos;
+
+ cur_leader_pmu_name = evsel__group_pmu_name(cur_leader);
+ if ((cur_leaders_grp != pos->core.leader && !force_grouped) ||
+ strcmp(cur_leader_pmu_name, pos_pmu_name)) {
+ /* Event is for a different group/PMU than last. */
+ cur_leader = pos;
+ /*
+ * Remember the leader's group before it is overwritten,
+ * so that later events match as being in the same
+ * group.
+ */
+ cur_leaders_grp = pos->core.leader;
+ }
+ pos_leader_pmu_name = evsel__group_pmu_name(pos_leader);
+ if (strcmp(pos_leader_pmu_name, pos_pmu_name) || force_grouped) {
+ /*
+ * Event's PMU differs from its leader's. Groups can't
+ * span PMUs, so update leader from the group/PMU
+ * tracker.
+ */
+ evsel__set_leader(pos, cur_leader);
+ }
+ }
+ list_for_each_entry(pos, list, core.node) {
+ pos->core.leader->nr_members++;
+ }
+}
+