perf evlist: Toggle merged_hybrid_group properly in enable/disable

Merging makes the merged event the leader of the events of the other
core PMUs so their histograms can be linked, but the result isn't a real
event group. Each event keeps its own file descriptor and has to be
enabled and disabled in its own right.

__evlist__enable(), __evlist__disable() and evlist__is_enabled() skip
anything that isn't a group leader, and the first two then walk the
group members of the events they do act on. For a merged set that is
backwards: the members are skipped by the leader test, so their file
descriptors are never touched, and the walk over the leader's members
only updates the bookkeeping in evsel->disabled.

Treat an evsel with merged_hybrid_group set as a leader so that it is
enabled and disabled in its own right via its own file descriptors.

'perf top' enables and disables by event name. A merged member carries
the name of its own PMU rather than the name that was asked for, so also
match it against its leader's name, otherwise naming the event toggles
only part of the merged set.

Signed-off-by: Ian Rogers <irogers@google.com>
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2026-09-16 22:07:04 -07:00 committed by Arnaldo Carvalho de Melo
parent aa2bad59fd
commit a25262fb16

View File

@ -753,7 +753,9 @@ static bool evlist__is_enabled(struct evlist *evlist)
struct evsel *pos;
evlist__for_each_entry(evlist, pos) {
if (!evsel__is_group_leader(pos) || !pos->core.fd)
if (!pos->core.fd)
continue;
if (!evsel__is_group_leader(pos) && !pos->merged_hybrid_group)
continue;
/* If at least one event is enabled, evlist is enabled. */
if (!pos->disabled)
@ -767,14 +769,19 @@ static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl
struct evsel *pos, *member;
struct evlist_cpu_iterator evlist_cpu_itr;
bool has_imm = false;
bool match;
/* Disable 'immediate' events last */
for (int imm = 0; imm <= 1; imm++) {
evlist__for_each_cpu(evlist_cpu_itr, evlist) {
pos = evlist_cpu_itr.evsel;
if (evsel__strcmp(pos, evsel_name))
match = !evsel__strcmp(pos, evsel_name);
if (!match && pos->merged_hybrid_group && evsel__leader(pos))
match = !evsel__strcmp(evsel__leader(pos), evsel_name);
if (!match)
continue;
if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd)
if (pos->disabled || (!evsel__is_group_leader(pos) &&
!pos->merged_hybrid_group) || !pos->core.fd)
continue;
if (excl_dummy && evsel__is_dummy_event(pos))
continue;
@ -789,9 +796,12 @@ static void __evlist__disable(struct evlist *evlist, char *evsel_name, bool excl
}
evlist__for_each_entry(evlist, pos) {
if (evsel__strcmp(pos, evsel_name))
match = !evsel__strcmp(pos, evsel_name);
if (!match && pos->merged_hybrid_group && evsel__leader(pos))
match = !evsel__strcmp(evsel__leader(pos), evsel_name);
if (!match)
continue;
if (!evsel__is_group_leader(pos) || !pos->core.fd)
if ((!evsel__is_group_leader(pos) && !pos->merged_hybrid_group) || !pos->core.fd)
continue;
if (excl_dummy && evsel__is_dummy_event(pos))
continue;
@ -827,21 +837,28 @@ static void __evlist__enable(struct evlist *evlist, char *evsel_name, bool excl_
{
struct evsel *pos, *member;
struct evlist_cpu_iterator evlist_cpu_itr;
bool match;
evlist__for_each_cpu(evlist_cpu_itr, evlist) {
pos = evlist_cpu_itr.evsel;
if (evsel__strcmp(pos, evsel_name))
match = !evsel__strcmp(pos, evsel_name);
if (!match && pos->merged_hybrid_group && evsel__leader(pos))
match = !evsel__strcmp(evsel__leader(pos), evsel_name);
if (!match)
continue;
if (!evsel__is_group_leader(pos) || !pos->core.fd)
if ((!evsel__is_group_leader(pos) && !pos->merged_hybrid_group) || !pos->core.fd)
continue;
if (excl_dummy && evsel__is_dummy_event(pos))
continue;
evsel__enable_cpu(pos, evlist_cpu_itr.cpu_map_idx);
}
evlist__for_each_entry(evlist, pos) {
if (evsel__strcmp(pos, evsel_name))
match = !evsel__strcmp(pos, evsel_name);
if (!match && pos->merged_hybrid_group && evsel__leader(pos))
match = !evsel__strcmp(evsel__leader(pos), evsel_name);
if (!match)
continue;
if (!evsel__is_group_leader(pos) || !pos->core.fd)
if ((!evsel__is_group_leader(pos) && !pos->merged_hybrid_group) || !pos->core.fd)
continue;
if (excl_dummy && evsel__is_dummy_event(pos))
continue;