[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1337801535-12865-3-git-send-email-jolsa@redhat.com>
Date: Wed, 23 May 2012 21:32:01 +0200
From: Jiri Olsa <jolsa@...hat.com>
To: acme@...hat.com, a.p.zijlstra@...llo.nl, mingo@...e.hu,
paulus@...ba.org, cjashfor@...ux.vnet.ibm.com, fweisbec@...il.com
Cc: eranian@...gle.com, gorcunov@...nvz.org, tzanussi@...il.com,
mhiramat@...hat.com, robert.richter@....com, fche@...hat.com,
linux-kernel@...r.kernel.org, masami.hiramatsu.pt@...achi.com,
drepper@...il.com, asharma@...com, benjamin.redelings@...cent.org,
Jiri Olsa <jolsa@...hat.com>
Subject: [PATCH 02/16] perf: Add ability to attach registers dump to sample
Introducing new sample_type bit PERF_SAMPLE_REGS. Once set,
the sample_regs value determines the kind of registers going
to be attached to the sample.
Currently only user level registers are supported, specified by
PERF_SAMPLE_REGS_USER sample_regs value. 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).
When PERF_SAMPLE_REGS and PERF_SAMPLE_REGS_USER are set, the
sample_regs_user bitmap lets a user choose a set of registers
to dump for the sample. The layout of this 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@...il.com>
Signed-off-by: Jiri Olsa <jolsa@...hat.com>
---
include/linux/perf_event.h | 25 ++++++++++-
kernel/events/core.c | 98 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+), 2 deletions(-)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index f325786..06f4b04 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -130,8 +130,9 @@ enum perf_event_sample_format {
PERF_SAMPLE_STREAM_ID = 1U << 9,
PERF_SAMPLE_RAW = 1U << 10,
PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ PERF_SAMPLE_REGS = 1U << 12,
- PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */
+ PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */
};
/*
@@ -163,6 +164,15 @@ enum perf_branch_sample_type {
PERF_SAMPLE_BRANCH_HV)
/*
+ * Values for sample_regs when PERF_SAMPLE_REGS is set.
+ * Defines register set to be attached to the sample.
+ */
+enum perf_sample_regs {
+ PERF_SAMPLE_REGS_USER = 1U << 0, /* user registers */
+ PERF_SAMPLE_REGS_MAX = 1U << 1, /* non-ABI */
+};
+
+/*
* The format of the data returned by read() on a perf event fd,
* as specified by attr.read_format:
*
@@ -271,7 +281,16 @@ 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 */
+
+ __u64 sample_regs; /* enum perf_sample_regs */
+
+ /*
+ * Arch specific mask for PERF_SAMPLE_REGS_USER setup.
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_user;
};
/*
@@ -607,6 +626,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>
#define PERF_MAX_STACK_DEPTH 255
@@ -1130,6 +1150,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 5b06cbb..e1d9e0c 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_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)
@@ -4011,6 +4042,30 @@ void perf_output_sample(struct perf_output_handle *handle,
perf_output_put(handle, nr);
}
}
+
+ if (sample_type & PERF_SAMPLE_REGS) {
+ u64 mode = event->attr.sample_regs;
+
+ if (mode & PERF_SAMPLE_REGS_USER) {
+ u64 id = PERF_REGS_ABI_NONE;
+
+ /*
+ * If there are no regs to dump, notice it through
+ * PERF_REGS_ABI_NONE id.
+ */
+ if (data->regs_user)
+ id = perf_reg_version();
+
+ perf_output_put(handle, id);
+
+ if (id) {
+ 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,
@@ -4062,6 +4117,24 @@ void perf_prepare_sample(struct perf_event_header *header,
}
header->size += size;
}
+
+ if (sample_type & PERF_SAMPLE_REGS) {
+ u64 mode = event->attr.sample_regs;
+ int size = 0;
+
+ if (mode & PERF_SAMPLE_REGS_USER) {
+ /* regs ID size */
+ 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,
@@ -6111,6 +6184,31 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
attr->branch_sample_type = mask;
}
}
+
+ if (attr->sample_type & PERF_SAMPLE_REGS) {
+ /* Mode must be specified. */
+ if (attr->sample_regs & ~(PERF_SAMPLE_REGS_MAX-1))
+ return -EINVAL;
+
+ /* Validate registers mask for user mode. */
+ if (attr->sample_regs & PERF_SAMPLE_REGS_USER)
+ ret = perf_reg_validate(attr->sample_regs_user);
+ else if (attr->sample_regs_user)
+ return -EINVAL;
+
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * Registers are not required in sample, all regs
+ * settings should be zero.
+ */
+ if (attr->sample_regs)
+ return -EINVAL;
+ if (attr->sample_regs_user)
+ return -EINVAL;
+ }
+
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@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists