Re: [RFC V1 11/11] perf: Capture branch privilege information

From: James Clark
Date: Wed Jan 26 2022 - 12:27:26 EST




On 24/01/2022 04:30, Anshuman Khandual wrote:
> Platforms like arm64 could capture privilege level information for all the
> branch records. Hence this adds a new element in the struct branch_entry to
> record the privilege level information, which could be requested through a
> new event.attr.branch_sample_type flag PERF_SAMPLE_BRANCH_PRIV_SAVE. While
> here, update the BRBE driver as required.
>
> Cc: Will Deacon <will@xxxxxxxxxx>
> Cc: Mark Rutland <mark.rutland@xxxxxxx>
> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
> Cc: Ingo Molnar <mingo@xxxxxxxxxx>
> Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
> Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
> Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
> Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
> Cc: linux-perf-users@xxxxxxxxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx
> Signed-off-by: Anshuman Khandual <anshuman.khandual@xxxxxxx>
> ---
> drivers/perf/arm_pmu_brbe.c | 28 ++++++++++++++++++++++++
> include/linux/perf_event.h | 5 +++++
> include/uapi/linux/perf_event.h | 13 ++++++++++-
> tools/include/uapi/linux/perf_event.h | 13 ++++++++++-
> tools/perf/Documentation/perf-record.txt | 1 +
> tools/perf/util/parse-branch-options.c | 1 +
> 6 files changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/perf/arm_pmu_brbe.c b/drivers/perf/arm_pmu_brbe.c
> index 7cd1208c6c58..d4cbea74c148 100644
> --- a/drivers/perf/arm_pmu_brbe.c
> +++ b/drivers/perf/arm_pmu_brbe.c
> @@ -270,6 +270,25 @@ static int brbe_fetch_perf_type(u64 brbinf)
> }
> }
>
> +static int brbe_fetch_perf_priv(u64 brbinf)
> +{
> + int brbe_el = brbe_fetch_el(brbinf);
> +
> + switch (brbe_el) {
> + case BRBINF_EL_EL0:
> + return PERF_BR_USER;
> + case BRBINF_EL_EL1:
> + return PERF_BR_KERNEL;
> + case BRBINF_EL_EL2:
> + if (is_kernel_in_hyp_mode())
> + return PERF_BR_KERNEL;
> + return PERF_BR_HV;
> + default:
> + pr_warn("unknown branch privilege captured\n");
> + return -1;
> + }
> +}
> +
> static void capture_brbe_flags(struct pmu_hw_events *cpuc, struct perf_event *event,
> u64 brbinf, int idx)
> {
> @@ -302,6 +321,15 @@ static void capture_brbe_flags(struct pmu_hw_events *cpuc, struct perf_event *ev
> cpuc->brbe_entries[idx].in_tx = brbinf & BRBINF_TX;
> }
> }
> +
> + if (branch_sample_priv(event)) {
> + /*
> + * All these information (i.e branch privilege level) are not
> + * available for source only branch records.
> + */
> + if (type != BRBINF_VALID_SOURCE)
> + cpuc->brbe_entries[idx].priv = brbe_fetch_perf_priv(brbinf);
> + }
> }
>
> /*
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 916ce5102b33..8021b6a30d86 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -1688,4 +1688,9 @@ static inline bool branch_sample_hw_index(const struct perf_event *event)
> {
> return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX;
> }
> +
> +static inline bool branch_sample_priv(const struct perf_event *event)
> +{
> + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_PRIV_SAVE;
> +}
> #endif /* _LINUX_PERF_EVENT_H */
> diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
> index 361fdc6b87a0..4d77710f7a4e 100644
> --- a/include/uapi/linux/perf_event.h
> +++ b/include/uapi/linux/perf_event.h
> @@ -204,6 +204,8 @@ enum perf_branch_sample_type_shift {
>
> PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */
>
> + PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT = 18, /* save privillege mode */
> +
> PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
> };
>
> @@ -233,6 +235,8 @@ enum perf_branch_sample_type {
>
> PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT,
>
> + PERF_SAMPLE_BRANCH_PRIV_SAVE = 1U << PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT,
> +
> PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
> };
>
> @@ -265,6 +269,12 @@ enum {
> PERF_BR_MAX,
> };
>
> +enum {
> + PERF_BR_USER = 0,
> + PERF_BR_KERNEL = 1,
> + PERF_BR_HV = 2,
> +};
> +

Can we have 0 as "UNKNOWN". It's going to be difficult to parse files when privilege information
isn't saved and get accurate results without that. For example if it's not set then presumably
the field would be 0 (PERF_BR_USER), but that doesn't mean the samples are user in that case.

I know you might be able to go backwards and look at what arguments were passed to the kernel but
it's not guaranteed that the kernel honored the request anyway. There are also other platforms
to think about etc.

If you look at the branch type definitions above they start at 0 (PERF_BR_UNKNOWN) which I think
works out quite nicely in the userspace code.

> #define PERF_SAMPLE_BRANCH_PLM_ALL \
> (PERF_SAMPLE_BRANCH_USER|\
> PERF_SAMPLE_BRANCH_KERNEL|\
> @@ -1377,7 +1387,8 @@ struct perf_branch_entry {
> abort:1, /* transaction abort */
> cycles:16, /* cycle count to last branch */
> type:6, /* branch type */
> - reserved:38;
> + priv:2, /* privilege level */> + reserved:36;
> };
>
> union perf_sample_weight {
> diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
> index 9a82b8aaed93..a2208400b0b9 100644
> --- a/tools/include/uapi/linux/perf_event.h
> +++ b/tools/include/uapi/linux/perf_event.h
> @@ -204,6 +204,8 @@ enum perf_branch_sample_type_shift {
>
> PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */
>
> + PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT = 18, /* save privillege mode */
> +
> PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
> };
>
> @@ -233,6 +235,8 @@ enum perf_branch_sample_type {
>
> PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT,
>
> + PERF_SAMPLE_BRANCH_PRIV_SAVE = 1U << PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT,
> +
> PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
> };
>
> @@ -265,6 +269,12 @@ enum {
> PERF_BR_MAX,
> };
>
> +enum {
> + PERF_BR_USER = 0,
> + PERF_BR_KERNEL = 1,
> + PERF_BR_HV = 2,
> +};
> +
> #define PERF_SAMPLE_BRANCH_PLM_ALL \
> (PERF_SAMPLE_BRANCH_USER|\
> PERF_SAMPLE_BRANCH_KERNEL|\
> @@ -1377,7 +1387,8 @@ struct perf_branch_entry {
> abort:1, /* transaction abort */
> cycles:16, /* cycle count to last branch */
> type:6, /* branch type */
> - reserved:38;
> + priv:2, /* privilege level */
> + reserved:36;
> };
>
> union perf_sample_weight {
> diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
> index 9ccc75935bc5..3e33686977a1 100644
> --- a/tools/perf/Documentation/perf-record.txt
> +++ b/tools/perf/Documentation/perf-record.txt
> @@ -387,6 +387,7 @@ following filters are defined:
> - abort_tx: only when the target is a hardware transaction abort
> - cond: conditional branches
> - save_type: save branch type during sampling in case binary is not available later
> + - priv: save privilege state during sampling in case binary is not available later
>
> +
> The option requires at least one branch type among any, any_call, any_ret, ind_call, cond.
> diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
> index bb4aa88c50a8..00588b9db474 100644
> --- a/tools/perf/util/parse-branch-options.c
> +++ b/tools/perf/util/parse-branch-options.c
> @@ -32,6 +32,7 @@ static const struct branch_mode branch_modes[] = {
> BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL),
> BRANCH_OPT("save_type", PERF_SAMPLE_BRANCH_TYPE_SAVE),
> BRANCH_OPT("stack", PERF_SAMPLE_BRANCH_CALL_STACK),
> + BRANCH_OPT("priv", PERF_SAMPLE_BRANCH_PRIV_SAVE),
> BRANCH_END
> };
>
>