Re: [PATCH 02/19] perf: Add ability to attach user level registersdump to sample

From: Stephane Eranian
Date: Wed Jun 13 2012 - 07:16:41 EST


On Mon, Jun 11, 2012 at 3:19 PM, Jiri Olsa <jolsa@xxxxxxxxxx> wrote:
> Introducing sample_regs_user bitmask into perf_event_attr
> struct to define the user level registers we want to attach
> to the sample. The dump itself is triggered once the
> sample_regs_user is not empty.
>
> Only user level registers are dump at the moment. Meaning the
> register values 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).
>
> The layout of the sample_regs_user bitmap is described in
> asm/perf_regs.h for archs that support register dump.
>
> This is going to be useful to bring Dwarf CFI based stack
> unwinding on top of samples.
>
> Signed-off-by: Frederic Weisbecker <fweisbec@xxxxxxxxx>
> Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
> ---
> Âinclude/linux/perf_event.h | Â 10 ++++++-
> Âkernel/events/core.c    |  61 ++++++++++++++++++++++++++++++++++++++++++++
> Â2 files changed, 70 insertions(+), 1 deletions(-)
>
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 1ce887a..d66cbeb 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
> @@ -271,7 +271,13 @@ struct perf_event_attr {
> Â Â Â Â Â Â Â Â__u64 Â Â Â Â Â bp_len;
> Â Â Â Â Â Â Â Â__u64 Â Â Â Â Â config2; /* extension of config1 */
> Â Â Â Â};
> - Â Â Â __u64 Â branch_sample_type; /* enum branch_sample_type */
> + Â Â Â __u64 Â branch_sample_type; /* enum perf_branch_sample_type */
> +
> + Â Â Â /*
> + Â Â Â Â* Defines set of user regs to dump on samples.
> + Â Â Â Â* See asm/perf_regs.h for details.
> + Â Â Â Â*/
> + Â Â Â __u64 Â sample_regs_user;
> Â};
That's not enough. You also need to define PERF_SAMPLE_USER_REGS
for sample_type. Although the sample_regs_users might look like it's enough
to capture regs, there is a problem when it comes to parsing the record. You
need an ordering guarantee that is explicitly spelled out in the API (the header
file). In your current patch, I have no way of knowing that sample_regs_users
are saved after BRANCH_STACK (should you have that enabled). Remember
that you can turn on/off sampled infos at will in sample_type. Yet to find the
infos when parsing, you need to know the order.

The enum perf_event_sample_format provides that ordering information. You
need to add a new type for sampling user regs.

>
> Â/*
> @@ -609,6 +615,7 @@ struct perf_guest_info_callbacks {
> Â#include <linux/static_key.h>
> Â#include <linux/atomic.h>
> Â#include <linux/sysfs.h>
> +#include <linux/perf_regs.h>
> Â#include <asm/local.h>
>
> Âstruct perf_callchain_entry {
> @@ -1131,6 +1138,7 @@ struct perf_sample_data {
>    Âstruct perf_callchain_entry   *callchain;
>    Âstruct perf_raw_record     Â*raw;
>    Âstruct perf_branch_stack    Â*br_stack;
> +    struct pt_regs         Â*regs_user;
> Â};
>
> Âstatic inline void perf_sample_data_init(struct perf_sample_data *data,
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index f85c015..e4df59d 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -3750,6 +3750,33 @@ 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 bit;
> +
> + Â Â Â for_each_set_bit(bit, (const unsigned long *) &mask,
> + Â Â Â Â Â Â Â Â Â Â Â Âsizeof(mask) * BITS_PER_BYTE) {
> + Â Â Â Â Â Â Â u64 val;
> +
> + Â Â Â Â Â Â Â val = perf_reg_value(regs, bit);
> + Â Â Â Â Â Â Â perf_output_put(handle, val);
> + Â Â Â }
> +}
> +
> +static struct pt_regs *perf_sample_regs_user(struct pt_regs *regs)
> +{
> + Â Â Â if (!user_mode(regs)) {
> + Â Â Â Â Â Â Â if (current->mm)
> + Â Â Â Â Â Â Â Â Â Â Â regs = task_pt_regs(current);
> + Â Â Â Â Â Â Â else
> + Â Â Â Â Â Â Â Â Â Â Â regs = NULL;
> + Â Â Â }
> +
> + Â Â Â return regs;
> +}
> +
> Âstatic void __perf_event_header__init_id(struct perf_event_header *header,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct perf_sample_data *data,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct perf_event *event)
> @@ -4010,6 +4037,23 @@ void perf_output_sample(struct perf_output_handle *handle,
> Â Â Â Â Â Â Â Â Â Â Â Âperf_output_put(handle, nr);
> Â Â Â Â Â Â Â Â}
> Â Â Â Â}
> +
> + Â Â Â if (event->attr.sample_regs_user) {
> + Â Â Â Â Â Â Â u64 avail = (data->regs_user != NULL);
> +
> + Â Â Â Â Â Â Â /*
> + Â Â Â Â Â Â Â Â* If there are no regs to dump, notice it through
> + Â Â Â Â Â Â Â Â* first u64 being zero.
> + Â Â Â Â Â Â Â Â*/
> + Â Â Â Â Â Â Â perf_output_put(handle, avail);
> +
> + Â Â Â Â Â Â Â if (avail) {
> + Â Â Â Â Â Â Â Â Â Â Â u64 mask = event->attr.sample_regs_user;
> + Â Â Â Â Â Â Â Â Â Â Â perf_output_sample_regs(handle,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â data->regs_user,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â mask);
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> Â}
>
> Âvoid perf_prepare_sample(struct perf_event_header *header,
> @@ -4061,6 +4105,19 @@ void perf_prepare_sample(struct perf_event_header *header,
> Â Â Â Â Â Â Â Â}
> Â Â Â Â Â Â Â Âheader->size += size;
> Â Â Â Â}
> +
> + Â Â Â if (event->attr.sample_regs_user) {
> + Â Â Â Â Â Â Â /* regs dump available bool */
> + Â Â Â Â Â Â Â int size = sizeof(u64);
> +
> + Â Â Â Â Â Â Â data->regs_user = perf_sample_regs_user(regs);
> + Â Â Â Â Â Â Â if (data->regs_user) {
> + Â Â Â Â Â Â Â Â Â Â Â u64 mask = event->attr.sample_regs_user;
> + Â Â Â Â Â Â Â Â Â Â Â size += hweight64(mask) * sizeof(u64);
> + Â Â Â Â Â Â Â }
> +
> + Â Â Â Â Â Â Â header->size += size;
> + Â Â Â }
> Â}
>
> Âstatic void perf_event_output(struct perf_event *event,
> @@ -6110,6 +6167,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
> Â Â Â Â Â Â Â Â Â Â Â Âattr->branch_sample_type = mask;
> Â Â Â Â Â Â Â Â}
> Â Â Â Â}
> +
> + Â Â Â if (attr->sample_regs_user)
> + Â Â Â Â Â Â Â ret = perf_reg_validate(attr->sample_regs_user);
> +
> Â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/