|
@@ -36,6 +36,7 @@
|
|
|
#include <linux/perf_event.h>
|
|
|
#include <linux/ftrace_event.h>
|
|
|
#include <linux/hw_breakpoint.h>
|
|
|
+#include <linux/mm_types.h>
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
@@ -3787,6 +3788,101 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Get remaining task size from user stack pointer.
|
|
|
+ *
|
|
|
+ * It'd be better to take stack vma map and limit this more
|
|
|
+ * precisly, but there's no way to get it safely under interrupt,
|
|
|
+ * so using TASK_SIZE as limit.
|
|
|
+ */
|
|
|
+static u64 perf_ustack_task_size(struct pt_regs *regs)
|
|
|
+{
|
|
|
+ unsigned long addr = perf_user_stack_pointer(regs);
|
|
|
+
|
|
|
+ if (!addr || addr >= TASK_SIZE)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return TASK_SIZE - addr;
|
|
|
+}
|
|
|
+
|
|
|
+static u16
|
|
|
+perf_sample_ustack_size(u16 stack_size, u16 header_size,
|
|
|
+ struct pt_regs *regs)
|
|
|
+{
|
|
|
+ u64 task_size;
|
|
|
+
|
|
|
+ /* No regs, no stack pointer, no dump. */
|
|
|
+ if (!regs)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if we fit in with the requested stack size into the:
|
|
|
+ * - TASK_SIZE
|
|
|
+ * If we don't, we limit the size to the TASK_SIZE.
|
|
|
+ *
|
|
|
+ * - remaining sample size
|
|
|
+ * If we don't, we customize the stack size to
|
|
|
+ * fit in to the remaining sample size.
|
|
|
+ */
|
|
|
+
|
|
|
+ task_size = min((u64) USHRT_MAX, perf_ustack_task_size(regs));
|
|
|
+ stack_size = min(stack_size, (u16) task_size);
|
|
|
+
|
|
|
+ /* 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));
|
|
|
+ }
|
|
|
+
|
|
|
+ return stack_size;
|
|
|
+}
|
|
|
+
|
|
|
+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 = perf_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 void __perf_event_header__init_id(struct perf_event_header *header,
|
|
|
struct perf_sample_data *data,
|
|
|
struct perf_event *event)
|
|
@@ -4064,6 +4160,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.regs);
|
|
|
}
|
|
|
|
|
|
void perf_prepare_sample(struct perf_event_header *header,
|
|
@@ -4129,6 +4230,35 @@ void perf_prepare_sample(struct perf_event_header *header,
|
|
|
|
|
|
header->size += size;
|
|
|
}
|
|
|
+
|
|
|
+ if (sample_type & PERF_SAMPLE_STACK_USER) {
|
|
|
+ /*
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+ struct perf_regs_user *uregs = &data->regs_user;
|
|
|
+ u16 stack_size = event->attr.sample_stack_user;
|
|
|
+ u16 size = sizeof(u64);
|
|
|
+
|
|
|
+ if (!uregs->abi)
|
|
|
+ perf_sample_regs_user(uregs, regs);
|
|
|
+
|
|
|
+ stack_size = perf_sample_ustack_size(stack_size, header->size,
|
|
|
+ uregs->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 (stack_size)
|
|
|
+ size += sizeof(u64) + stack_size;
|
|
|
+
|
|
|
+ data->stack_user_size = stack_size;
|
|
|
+ header->size += size;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void perf_event_output(struct perf_event *event,
|
|
@@ -6205,8 +6335,26 @@ 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) {
|
|
|
+ if (!arch_perf_have_user_stack_dump())
|
|
|
+ return -ENOSYS;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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;
|