lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1335958638-5160-3-git-send-email-jolsa@redhat.com>
Date:	Wed,  2 May 2012 13:37:03 +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, Jiri Olsa <jolsa@...hat.com>
Subject: [PATCH 02/17] 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 ddbb6a9..6a8c974 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, u64 addr)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 32cfc76..dd99718 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,
@@ -6112,6 +6185,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

Powered by Openwall GNU/*/Linux Powered by OpenVZ