Skip to content

Fall back to plain hardware perf events when extended types are rejected - #2367

Merged
LebedevRI merged 6 commits into
google:mainfrom
charlesmunger:fix_android_pmu
Nov 10, 2026
Merged

LebedevRI merged 6 commits into
google:mainfrom
charlesmunger:fix_android_pmu

Conversation

@charlesmunger

Copy link
Copy Markdown
Contributor

Since "Replicate generic hardware events on all CPU PMUs" (#2123), generic hardware events are opened on every CPU PMU with the PMU type in the upper bits of attr.config (PERF_PMU_TYPE_SHIFT). That needs the PMU driver to advertise PERF_PMU_CAP_EXTENDED_HW_TYPE, which e.g. arm_pmu in Linux 6.1 does not, so the kernel rejects such events (with EINVAL on an arm64 emulator, ENOENT on a Pixel 9). There was no fallback, so every hardware counter was dropped with "Failed to get a file descriptor for performance counter" and Android benchmarks reported no PMU data.

If a hardware event cannot be opened on any CPU PMU with an extended type, open it as a plain hardware event instead, and stop trying extended types for the remaining counters. Failures on extended types are logged at --v=1 rather than as warnings, since the fallback may succeed.

On heterogeneous systems without extended type support, the kernel opens the plain event on a single CPU PMU of its choosing, so it only counts while the benchmark runs on that PMU's CPUs (as before #2123).

Also log CPU PMU discovery and perf_event_open results at --v=1:

perf_counters: found CPU PMU armv8_pmuv3 type 8
perf_counters: could not open CYCLES (pmu 8, type 0, config 0x800000000): Invalid argument
perf_counters: extended hardware event types appear to be unsupported, falling back to plain hardware events
perf_counters: opened CYCLES (pmu 0) as fd 3

AI disclosure: this change was written with the help of an AI coding tool.

Since "Replicate generic hardware events on all CPU PMUs" (google#2123), generic
hardware events are opened on every CPU PMU with the PMU type in the upper
bits of attr.config (PERF_PMU_TYPE_SHIFT). That needs the PMU driver to
advertise PERF_PMU_CAP_EXTENDED_HW_TYPE, which e.g. arm_pmu in Linux 6.1
does not, so the kernel rejects such events (with EINVAL on an arm64
emulator, ENOENT on a Pixel 9). There was no fallback, so every hardware
counter was dropped with "Failed to get a file descriptor for performance
counter" and Android benchmarks reported no PMU data.

If a hardware event cannot be opened on any CPU PMU with an extended type,
open it as a plain hardware event instead, and stop trying extended types
for the remaining counters. Failures on extended types are logged at --v=1
rather than as warnings, since the fallback may succeed.

On heterogeneous systems without extended type support, the kernel opens
the plain event on a single CPU PMU of its choosing, so it only counts
while the benchmark runs on that PMU's CPUs (as before google#2123).

Also log CPU PMU discovery and perf_event_open results at --v=1:

  perf_counters: found CPU PMU armv8_pmuv3 type 8
  perf_counters: could not open CYCLES (pmu 8, type 0, config 0x800000000): Invalid argument
  perf_counters: extended hardware event types appear to be unsupported, falling back to plain hardware events
  perf_counters: opened CYCLES (pmu 0) as fd 3

AI disclosure: this change was written with the help of an AI coding tool.
Comment thread src/perf_counters.cc Outdated
pmus.push_back(0);
}
continue;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this function be refactored somehow to avoid this try_extended_hw_type thing?
This approach looks convoluted.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have clarified. Can this be refactored to avoid doing perf_event_open() just to close it again,
and then immediately reopen it again?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the new commit look like what you're expecting?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is much more reasonable, yes. Thank you.

Comment thread src/perf_counters.cc Resolved
Comment thread src/perf_counters.cc Resolved
Instead of falling back to a plain hardware event inside the open loop
(which needed the try_extended_hw_type flag, an index-based loop that
appends to the PMU list, and restoring group_id), move the code that
opens a counter (with retries and the fallback to a new group) into a
helper that returns 0 if the counter could be opened, or else the errno
of the failed perf_event_open call. Use it to first try opening each
generic hardware event on all CPU PMUs with extended types and, if none
of these copies can be opened, to open it as a plain hardware event, as
before "Replicate generic hardware events on all CPU PMUs" (google#2123). Each
event is only opened once, and no state is carried from one counter to
the next.

Since the helper only updates group_id when a counter is opened, a
counter that cannot be opened no longer forces the following counters
into a new group.

The --v=1 logging of every perf_event_open call is replaced by logging
the copies that cannot be opened, e.g.:

  perf_counters: found CPU PMU armv8_pmuv3 type 8
  perf_counters: cannot open CYCLES on PMU type 8: Invalid argument

AI disclosure: this change was written with the help of an AI coding tool.
Comment thread src/perf_counters.cc
Comment on lines +271 to +272
} else {
opened = true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected behaviour?
Should the fallback happen if none of GetPMUTypesForEvent() succeeded,
or if at least one failed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added clarifying comment

Comment thread src/perf_counters.cc Resolved
* Keep GetPMUTypesForEvent() as before, only dropping the default {0}
  (non-replicated events now take the plain-event path in the caller).
* Document that the plain event is only opened if the event could not
  be opened on any CPU PMU (or is not replicated): also opening it on
  some PMUs would count twice on the PMU that the kernel opens the plain
  event on, since the counts of all PMUs are summed.

No functional change.

AI disclosure: this change was written with the help of an AI coding tool.
LebedevRI
LebedevRI previously approved these changes Nov 7, 2026

@LebedevRI LebedevRI left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming add_counter is a NFC code extraction modulo PERF_PMU_TYPE_SHIFT, LG.

LebedevRI and others added 3 commits November 7, 2026 04:06
The previous change accidentally reformatted `struct perf_event_attr
attr {};` to `attr{}`, as newer clang-format versions do. The
clang-format-lint check uses clang-format 18, which formats it as
`attr {}`.

AI disclosure: this change was written with the help of an AI coding tool.
@charlesmunger

Copy link
Copy Markdown
Contributor Author

Fixed clang format issue, should be ready for merge now

@LebedevRI LebedevRI left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as there's nothing funny hidden in the diff, seems fine.

@LebedevRI
LebedevRI merged commit 438873e into google:main Nov 10, 2026
108 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants