Re: [PATCH 03/16] perf: Add ability to dump user regs

From: Stephane Eranian
Date: Mon Apr 23 2012 - 06:15:27 EST


On Tue, Apr 17, 2012 at 1:17 PM, Jiri Olsa <jolsa@xxxxxxxxxx> wrote:
> Add new attr->user_regs bitmap that lets a user choose a set
> of user registers to dump to the sample. The layout of this
> bitmap is described in asm/perf_regs.h for archs that
> support register dump.
>
> The perf syscall will fail if attr->user_regs is non zero.
>
> The register value here are those of the user space context as
> it was before the user entered the kernel for whatever reason
> (syscall, irq, exception, or a PMI happening in userspace).
>
> This is going to be useful to bring Dwarf CFI based stack unwinding
> on top of samples.
>
> TODO handle compat tasks
>
> Signed-off-by: Frederic Weisbecker <fweisbec@xxxxxxxxx>
> Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
> ---
> Âinclude/linux/perf_event.h | Â Â8 +++++
> Âkernel/events/core.c    |  63 ++++++++++++++++++++++++++++++++++++++++++++
> Â2 files changed, 71 insertions(+), 0 deletions(-)
>
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index ddbb6a9..c63b807 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -272,6 +272,12 @@ struct perf_event_attr {
> Â Â Â Â Â Â Â Â__u64 Â Â Â Â Â config2; /* extension of config1 */
> Â Â Â Â};
> Â Â Â Â__u64 Â branch_sample_type; /* enum branch_sample_type */
> +
> + Â Â Â /*
> + Â Â Â Â* Arch specific mask that defines a set of user regs to dump on
> + Â Â Â Â* samples. See asm/perf_regs.h for details.
> + Â Â Â Â*/
> + Â Â Â __u64 Â Â Â Â Â Â Â Â Â user_regs;
> Â};
>
Don't like the name of the field too much. You be more explicit.
Something like user_sample_regs.

> Â/*
> @@ -608,6 +614,7 @@ struct perf_guest_info_callbacks {
> Â#include <linux/atomic.h>
> Â#include <linux/sysfs.h>
> Â#include <asm/local.h>
> +#include <asm/perf_regs.h>
>
> Â#define PERF_MAX_STACK_DEPTH Â Â Â Â Â 255
>
> @@ -1130,6 +1137,7 @@ struct perf_sample_data {
>    Âstruct perf_callchain_entry   *callchain;
>    Âstruct perf_raw_record     Â*raw;
>    Âstruct perf_branch_stack    Â*br_stack;
> +    struct pt_regs         Â*uregs;
> Â};
>
> Âstatic inline void perf_sample_data_init(struct perf_sample_data *data, u64 addr)
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index a6a9ec4..9f29fc3 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -3751,6 +3751,37 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
> Â}
> ÂEXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
>
> +static void
> +perf_output_sample_regs(struct perf_output_handle *handle,
> + Â Â Â Â Â Â Â Â Â Â Â struct pt_regs *regs, u64 mask)
> +{
> + Â Â Â int i = 0;
> +
> + Â Â Â do {
> + Â Â Â Â Â Â Â u64 val;
> +
> + Â Â Â Â Â Â Â if (mask & 1) {
> + Â Â Â Â Â Â Â Â Â Â Â val = perf_reg_value(regs, i);
> + Â Â Â Â Â Â Â Â Â Â Â perf_output_put(handle, val);
> + Â Â Â Â Â Â Â }
> +
> + Â Â Â Â Â Â Â mask >>= 1;
> + Â Â Â Â Â Â Â i++;
> + Â Â Â } while (mask);
> +}
> +
> +static struct pt_regs *perf_sample_uregs(struct pt_regs *regs)
> +{
> + Â Â Â if (!user_mode(regs)) {
> + Â Â Â Â Â Â Â if (current->mm)
> + Â Â Â Â Â Â Â Â Â Â Â regs = task_pt_regs(current);
> + Â Â Â Â Â Â Â else
> + Â Â Â Â Â Â Â Â Â Â Â regs = NULL;
> + Â Â Â }
> +
> + Â Â Â return regs;
> +}
> +
You are assuming the user app is running the same ABI than that of the
kernel. That's not correct on X86 and possibly other architectures.

> Âstatic void __perf_event_header__init_id(struct perf_event_header *header,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct perf_sample_data *data,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct perf_event *event)
> @@ -4011,6 +4042,21 @@ void perf_output_sample(struct perf_output_handle *handle,
> Â Â Â Â Â Â Â Â Â Â Â Âperf_output_put(handle, nr);
> Â Â Â Â Â Â Â Â}
> Â Â Â Â}
> +
> + Â Â Â if (event->attr.user_regs) {
> + Â Â Â Â Â Â Â u64 id;
> +
> + Â Â Â Â Â Â Â /*
> + Â Â Â Â Â Â Â Â* If there are no regs to dump, notice it through a
> + Â Â Â Â Â Â Â Â* PERF_REGS_VERSION_NONE version.
> + Â Â Â Â Â Â Â Â*/
> + Â Â Â Â Â Â Â id = data->uregs ? perf_reg_version() : PERF_REGS_VERSION_NONE;
> + Â Â Â Â Â Â Â perf_output_put(handle, id);
> +
> + Â Â Â Â Â Â Â if (id)
> + Â Â Â Â Â Â Â Â Â Â Â perf_output_sample_regs(handle, data->uregs,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â event->attr.user_regs);
> + Â Â Â }
> Â}
>
> Âvoid perf_prepare_sample(struct perf_event_header *header,
> @@ -4062,6 +4108,16 @@ void perf_prepare_sample(struct perf_event_header *header,
> Â Â Â Â Â Â Â Â}
> Â Â Â Â Â Â Â Âheader->size += size;
> Â Â Â Â}
> +
> + Â Â Â if (event->attr.user_regs) {
> + Â Â Â Â Â Â Â int size = sizeof(u64); /* the version size */
> +
> + Â Â Â Â Â Â Â data->uregs = perf_sample_uregs(regs);
> + Â Â Â Â Â Â Â if (data->uregs)
> + Â Â Â Â Â Â Â Â Â Â Â size += hweight64(event->attr.user_regs) * sizeof(u64);
> +
> + Â Â Â Â Â Â Â header->size += size;
> + Â Â Â }
> Â}
>
> Âstatic void perf_event_output(struct perf_event *event,
> @@ -6112,6 +6168,13 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
> Â Â Â Â Â Â Â Â Â Â Â Âattr->branch_sample_type = mask;
> Â Â Â Â Â Â Â Â}
> Â Â Â Â}
> +
> + Â Â Â /*
> + Â Â Â Â* Don't let throught invalid register mask (i.e. the architecture
> + Â Â Â Â* does not support register dump at all).
> + Â Â Â Â*/
> + Â Â Â ret = perf_reg_validate(attr->user_regs);
> +
Again, how do you deal with 32-bit apps vs. 64-bit kernel?

> Âout:
> Â Â Â Âreturn ret;
>
> --
> 1.7.7.6
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/