lkml.org 
[lkml]   [2012]   [Jun]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 07/23] perf: Add ability to attach user stack dump to sample
    Date
    Introducing PERF_SAMPLE_STACK_USER sample type bit to trigger
    the dump of the user level stack on sample. The size of the
    dump is specified by sample_stack_user value.

    Being able to dump parts of the user stack, starting from the
    stack pointer, will be useful to make a post mortem dwarf CFI
    based stack unwinding.

    Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
    Signed-off-by: Jiri Olsa <jolsa@redhat.com>
    ---
    include/linux/perf_event.h | 18 ++++++-
    kernel/events/core.c | 120 +++++++++++++++++++++++++++++++++++++++++++-
    2 files changed, 136 insertions(+), 2 deletions(-)

    diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
    index 024a822..62d6743 100644
    --- a/include/linux/perf_event.h
    +++ b/include/linux/perf_event.h
    @@ -131,8 +131,9 @@ enum perf_event_sample_format {
    PERF_SAMPLE_RAW = 1U << 10,
    PERF_SAMPLE_BRANCH_STACK = 1U << 11,
    PERF_SAMPLE_REGS_USER = 1U << 12,
    + PERF_SAMPLE_STACK_USER = 1U << 13,

    - PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */
    + PERF_SAMPLE_MAX = 1U << 14, /* non-ABI */
    };

    /*
    @@ -196,6 +197,7 @@ enum perf_event_read_format {
    #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
    #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
    #define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */
    +#define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */

    /*
    * Hardware event_id to monitor via a performance monitoring event:
    @@ -280,6 +282,14 @@ struct perf_event_attr {
    * See asm/perf_regs.h for details.
    */
    __u64 sample_regs_user;
    +
    + /*
    + * Defines size of the user stack to dump on samples.
    + */
    + __u32 sample_stack_user;
    +
    + /* Align to u64. */
    + __u32 __reserved_2;
    };

    /*
    @@ -559,6 +569,10 @@ enum perf_event_type {
    *
    * { u64 available;
    * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
    + *
    + * { u64 size;
    + * char data[size];
    + * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
    * };
    */
    PERF_RECORD_SAMPLE = 9,
    @@ -1144,6 +1158,7 @@ struct perf_sample_data {
    struct perf_raw_record *raw;
    struct perf_branch_stack *br_stack;
    struct pt_regs *regs_user;
    + u64 stack_user_size;
    };

    static inline void perf_sample_data_init(struct perf_sample_data *data,
    @@ -1155,6 +1170,7 @@ static inline void perf_sample_data_init(struct perf_sample_data *data,
    data->br_stack = NULL;
    data->period = period;
    data->regs_user = NULL;
    + data->stack_user_size = 0;
    }

    extern void perf_output_sample(struct perf_output_handle *handle,
    diff --git a/kernel/events/core.c b/kernel/events/core.c
    index 7df37e0..68c1f02 100644
    --- a/kernel/events/core.c
    +++ b/kernel/events/core.c
    @@ -3765,6 +3765,45 @@ perf_output_sample_regs(struct perf_output_handle *handle,
    }
    }

    +static void
    +perf_output_sample_ustack(struct perf_output_handle *handle, u64 dump_size,
    + struct pt_regs *regs)
    +{
    + /* Case of a kernel thread, nothing to dump */
    + if (!regs) {
    + u64 size = 0;
    + perf_output_put(handle, size);
    + } else {
    + unsigned long sp;
    + unsigned int rem;
    + u64 dyn_size;
    +
    + /*
    + * We dump:
    + * static size
    + * - the size requested by user or the best one we can fit
    + * in to the sample max size
    + * data
    + * - user stack dump data
    + * dynamic size
    + * - the actual dumped size
    + */
    +
    + /* Static size. */
    + perf_output_put(handle, dump_size);
    +
    + /* Data. */
    + sp = user_stack_pointer(regs);
    + rem = __output_copy_user(handle, (void *) sp, dump_size);
    + dyn_size = dump_size - rem;
    +
    + perf_output_skip(handle, rem);
    +
    + /* Dynamic size. */
    + perf_output_put(handle, dyn_size);
    + }
    +}
    +
    static struct pt_regs *perf_sample_regs_user(struct pt_regs *regs)
    {
    if (!user_mode(regs)) {
    @@ -4054,6 +4093,11 @@ void perf_output_sample(struct perf_output_handle *handle,
    mask);
    }
    }
    +
    + if (sample_type & PERF_SAMPLE_STACK_USER)
    + perf_output_sample_ustack(handle,
    + data->stack_user_size,
    + data->regs_user);
    }

    void perf_prepare_sample(struct perf_event_header *header,
    @@ -4118,6 +4162,65 @@ void perf_prepare_sample(struct perf_event_header *header,

    header->size += size;
    }
    +
    + if (sample_type & PERF_SAMPLE_STACK_USER) {
    + /*
    + * A first field that tells the _static_ size of the
    + * dump. 0 if there is nothing to dump (ie: we are in
    + * a kernel thread) otherwise the requested size.
    + */
    + u16 size = sizeof(u64);
    + u16 stack_size = 0;
    +
    + if (!data->regs_user)
    + data->regs_user = perf_sample_regs_user(regs);
    +
    + /*
    + * If there is something to dump, add space for the
    + * dump itself and for the field that tells the
    + * dynamic size, which is how many have been actually
    + * dumped.
    + */
    + if (data->regs_user) {
    + /*
    + * XXX We need to check if we fit in with the
    + * requested stack size into the sample. If we
    + * don't, we customize the stack size to fit in.
    + *
    + * Either we need PERF_SAMPLE_STACK_USER bit to
    + * be allways processed as the last one or have
    + * additional check added in case new sample type
    + * is added, because we could eat up the rest of
    + * the sample size.
    + */
    +
    + u16 header_size = header->size;
    +
    + stack_size = (u16) event->attr.sample_stack_user;
    +
    + /*
    + * Current header size plus static size and
    + * dynamic size.
    + */
    + header_size += 2 * sizeof(u64);
    +
    + /* Do we fit in with the current stack dump size? */
    + if ((u16) (header_size + stack_size) < header_size) {
    + /*
    + * If we overflow the maximum size for the
    + * sample, we customize the stack dump size
    + * to fit in.
    + */
    + stack_size = USHRT_MAX - header_size - sizeof(u64);
    + stack_size = round_up(stack_size, sizeof(u64));
    + }
    +
    + size += sizeof(u64) + stack_size;
    + }
    +
    + data->stack_user_size = stack_size;
    + header->size += size;
    + }
    }

    static void perf_event_output(struct perf_event *event,
    @@ -6168,8 +6271,23 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
    }
    }

    - if (attr->sample_type & PERF_SAMPLE_REGS_USER)
    + if (attr->sample_type & PERF_SAMPLE_REGS_USER) {
    ret = perf_reg_validate(attr->sample_regs_user);
    + if (ret)
    + return ret;
    + }
    +
    + if (attr->sample_type & PERF_SAMPLE_STACK_USER) {
    + /*
    + * We have __u32 type for the size, but so far
    + * we can only use __u16 as maximum due to the
    + * __u16 sample size limit.
    + */
    + if (attr->sample_stack_user >= USHRT_MAX)
    + ret = -EINVAL;
    + else if (!IS_ALIGNED(attr->sample_stack_user, sizeof(u64)))
    + ret = -EINVAL;
    + }

    out:
    return ret;
    --
    1.7.7.6


    \
     
     \ /
      Last update: 2012-06-19 18:01    [W:4.769 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site