aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Olsa <jolsa@redhat.com>2012-08-07 09:20:37 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2012-08-10 10:31:26 -0400
commit4018994f3d8785275ef0e7391b75c3462c029e56 (patch)
tree661b5c488977c5150eb9d1ac4cec944309c83a16
parentc5e63197db519bae1c33e41ea0342a50f39e7a93 (diff)
perf: Add ability to attach user level registers dump to sample
Introducing PERF_SAMPLE_REGS_USER sample type bit to trigger the dump of user level registers on sample. Registers we want to dump are specified by sample_regs_user bitmask. Only user level registers are dumped 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. Original-patch-by: Frederic Weisbecker <fweisbec@gmail.com> [ Dump registers ABI specification. ] Signed-off-by: Jiri Olsa <jolsa@redhat.com> Suggested-by: Stephane Eranian <eranian@google.com> Cc: "Frank Ch. Eigler" <fche@redhat.com> Cc: Arun Sharma <asharma@fb.com> Cc: Benjamin Redelings <benjamin.redelings@nescent.org> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: Cyrill Gorcunov <gorcunov@openvz.org> Cc: Frank Ch. Eigler <fche@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Robert Richter <robert.richter@amd.com> Cc: Stephane Eranian <eranian@google.com> Cc: Tom Zanussi <tzanussi@gmail.com> Cc: Ulrich Drepper <drepper@gmail.com> Link: http://lkml.kernel.org/r/1344345647-11536-3-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--arch/x86/kernel/perf_regs.c15
-rw-r--r--include/linux/perf_event.h35
-rw-r--r--include/linux/perf_regs.h6
-rw-r--r--kernel/events/core.c66
4 files changed, 119 insertions, 3 deletions
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
index 3d6923528b1c..c5a3e5cfe07f 100644
--- a/arch/x86/kernel/perf_regs.c
+++ b/arch/x86/kernel/perf_regs.c
@@ -1,5 +1,7 @@
1#include <linux/errno.h> 1#include <linux/errno.h>
2#include <linux/kernel.h> 2#include <linux/kernel.h>
3#include <linux/sched.h>
4#include <linux/perf_event.h>
3#include <linux/bug.h> 5#include <linux/bug.h>
4#include <linux/stddef.h> 6#include <linux/stddef.h>
5#include <asm/perf_regs.h> 7#include <asm/perf_regs.h>
@@ -71,6 +73,11 @@ int perf_reg_validate(u64 mask)
71 73
72 return 0; 74 return 0;
73} 75}
76
77u64 perf_reg_abi(struct task_struct *task)
78{
79 return PERF_SAMPLE_REGS_ABI_32;
80}
74#else /* CONFIG_X86_64 */ 81#else /* CONFIG_X86_64 */
75#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ 82#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
76 (1ULL << PERF_REG_X86_ES) | \ 83 (1ULL << PERF_REG_X86_ES) | \
@@ -87,4 +94,12 @@ int perf_reg_validate(u64 mask)
87 94
88 return 0; 95 return 0;
89} 96}
97
98u64 perf_reg_abi(struct task_struct *task)
99{
100 if (test_tsk_thread_flag(task, TIF_IA32))
101 return PERF_SAMPLE_REGS_ABI_32;
102 else
103 return PERF_SAMPLE_REGS_ABI_64;
104}
90#endif /* CONFIG_X86_32 */ 105#endif /* CONFIG_X86_32 */
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 7602ccb3f40e..3d4d84745f07 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -130,8 +130,9 @@ enum perf_event_sample_format {
130 PERF_SAMPLE_STREAM_ID = 1U << 9, 130 PERF_SAMPLE_STREAM_ID = 1U << 9,
131 PERF_SAMPLE_RAW = 1U << 10, 131 PERF_SAMPLE_RAW = 1U << 10,
132 PERF_SAMPLE_BRANCH_STACK = 1U << 11, 132 PERF_SAMPLE_BRANCH_STACK = 1U << 11,
133 PERF_SAMPLE_REGS_USER = 1U << 12,
133 134
134 PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */ 135 PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */
135}; 136};
136 137
137/* 138/*
@@ -163,6 +164,15 @@ enum perf_branch_sample_type {
163 PERF_SAMPLE_BRANCH_HV) 164 PERF_SAMPLE_BRANCH_HV)
164 165
165/* 166/*
167 * Values to determine ABI of the registers dump.
168 */
169enum perf_sample_regs_abi {
170 PERF_SAMPLE_REGS_ABI_NONE = 0,
171 PERF_SAMPLE_REGS_ABI_32 = 1,
172 PERF_SAMPLE_REGS_ABI_64 = 2,
173};
174
175/*
166 * The format of the data returned by read() on a perf event fd, 176 * The format of the data returned by read() on a perf event fd,
167 * as specified by attr.read_format: 177 * as specified by attr.read_format:
168 * 178 *
@@ -194,6 +204,7 @@ enum perf_event_read_format {
194#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ 204#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
195#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ 205#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
196#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ 206#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
207#define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */
197 208
198/* 209/*
199 * Hardware event_id to monitor via a performance monitoring event: 210 * Hardware event_id to monitor via a performance monitoring event:
@@ -271,7 +282,13 @@ struct perf_event_attr {
271 __u64 bp_len; 282 __u64 bp_len;
272 __u64 config2; /* extension of config1 */ 283 __u64 config2; /* extension of config1 */
273 }; 284 };
274 __u64 branch_sample_type; /* enum branch_sample_type */ 285 __u64 branch_sample_type; /* enum perf_branch_sample_type */
286
287 /*
288 * Defines set of user regs to dump on samples.
289 * See asm/perf_regs.h for details.
290 */
291 __u64 sample_regs_user;
275}; 292};
276 293
277/* 294/*
@@ -548,6 +565,9 @@ enum perf_event_type {
548 * char data[size];}&& PERF_SAMPLE_RAW 565 * char data[size];}&& PERF_SAMPLE_RAW
549 * 566 *
550 * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK 567 * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
568 *
569 * { u64 abi; # enum perf_sample_regs_abi
570 * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
551 * }; 571 * };
552 */ 572 */
553 PERF_RECORD_SAMPLE = 9, 573 PERF_RECORD_SAMPLE = 9,
@@ -609,6 +629,7 @@ struct perf_guest_info_callbacks {
609#include <linux/static_key.h> 629#include <linux/static_key.h>
610#include <linux/atomic.h> 630#include <linux/atomic.h>
611#include <linux/sysfs.h> 631#include <linux/sysfs.h>
632#include <linux/perf_regs.h>
612#include <asm/local.h> 633#include <asm/local.h>
613 634
614struct perf_callchain_entry { 635struct perf_callchain_entry {
@@ -654,6 +675,11 @@ struct perf_branch_stack {
654 struct perf_branch_entry entries[0]; 675 struct perf_branch_entry entries[0];
655}; 676};
656 677
678struct perf_regs_user {
679 __u64 abi;
680 struct pt_regs *regs;
681};
682
657struct task_struct; 683struct task_struct;
658 684
659/* 685/*
@@ -1133,6 +1159,7 @@ struct perf_sample_data {
1133 struct perf_callchain_entry *callchain; 1159 struct perf_callchain_entry *callchain;
1134 struct perf_raw_record *raw; 1160 struct perf_raw_record *raw;
1135 struct perf_branch_stack *br_stack; 1161 struct perf_branch_stack *br_stack;
1162 struct perf_regs_user regs_user;
1136}; 1163};
1137 1164
1138static inline void perf_sample_data_init(struct perf_sample_data *data, 1165static inline void perf_sample_data_init(struct perf_sample_data *data,
@@ -1142,7 +1169,9 @@ static inline void perf_sample_data_init(struct perf_sample_data *data,
1142 data->addr = addr; 1169 data->addr = addr;
1143 data->raw = NULL; 1170 data->raw = NULL;
1144 data->br_stack = NULL; 1171 data->br_stack = NULL;
1145 data->period = period; 1172 data->period = period;
1173 data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE;
1174 data->regs_user.regs = NULL;
1146} 1175}
1147 1176
1148extern void perf_output_sample(struct perf_output_handle *handle, 1177extern void perf_output_sample(struct perf_output_handle *handle,
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h
index a2f1a98f7839..3c73d5fe18be 100644
--- a/include/linux/perf_regs.h
+++ b/include/linux/perf_regs.h
@@ -5,6 +5,7 @@
5#include <asm/perf_regs.h> 5#include <asm/perf_regs.h>
6u64 perf_reg_value(struct pt_regs *regs, int idx); 6u64 perf_reg_value(struct pt_regs *regs, int idx);
7int perf_reg_validate(u64 mask); 7int perf_reg_validate(u64 mask);
8u64 perf_reg_abi(struct task_struct *task);
8#else 9#else
9static inline u64 perf_reg_value(struct pt_regs *regs, int idx) 10static inline u64 perf_reg_value(struct pt_regs *regs, int idx)
10{ 11{
@@ -15,5 +16,10 @@ static inline int perf_reg_validate(u64 mask)
15{ 16{
16 return mask ? -ENOSYS : 0; 17 return mask ? -ENOSYS : 0;
17} 18}
19
20static inline u64 perf_reg_abi(struct task_struct *task)
21{
22 return PERF_SAMPLE_REGS_ABI_NONE;
23}
18#endif /* CONFIG_HAVE_PERF_REGS */ 24#endif /* CONFIG_HAVE_PERF_REGS */
19#endif /* _LINUX_PERF_REGS_H */ 25#endif /* _LINUX_PERF_REGS_H */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index b7935fcec7d9..d3ce97525b9f 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -3756,6 +3756,37 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
3756} 3756}
3757EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); 3757EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
3758 3758
3759static void
3760perf_output_sample_regs(struct perf_output_handle *handle,
3761 struct pt_regs *regs, u64 mask)
3762{
3763 int bit;
3764
3765 for_each_set_bit(bit, (const unsigned long *) &mask,
3766 sizeof(mask) * BITS_PER_BYTE) {
3767 u64 val;
3768
3769 val = perf_reg_value(regs, bit);
3770 perf_output_put(handle, val);
3771 }
3772}
3773
3774static void perf_sample_regs_user(struct perf_regs_user *regs_user,
3775 struct pt_regs *regs)
3776{
3777 if (!user_mode(regs)) {
3778 if (current->mm)
3779 regs = task_pt_regs(current);
3780 else
3781 regs = NULL;
3782 }
3783
3784 if (regs) {
3785 regs_user->regs = regs;
3786 regs_user->abi = perf_reg_abi(current);
3787 }
3788}
3789
3759static void __perf_event_header__init_id(struct perf_event_header *header, 3790static void __perf_event_header__init_id(struct perf_event_header *header,
3760 struct perf_sample_data *data, 3791 struct perf_sample_data *data,
3761 struct perf_event *event) 3792 struct perf_event *event)
@@ -4016,6 +4047,23 @@ void perf_output_sample(struct perf_output_handle *handle,
4016 perf_output_put(handle, nr); 4047 perf_output_put(handle, nr);
4017 } 4048 }
4018 } 4049 }
4050
4051 if (sample_type & PERF_SAMPLE_REGS_USER) {
4052 u64 abi = data->regs_user.abi;
4053
4054 /*
4055 * If there are no regs to dump, notice it through
4056 * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE).
4057 */
4058 perf_output_put(handle, abi);
4059
4060 if (abi) {
4061 u64 mask = event->attr.sample_regs_user;
4062 perf_output_sample_regs(handle,
4063 data->regs_user.regs,
4064 mask);
4065 }
4066 }
4019} 4067}
4020 4068
4021void perf_prepare_sample(struct perf_event_header *header, 4069void perf_prepare_sample(struct perf_event_header *header,
@@ -4067,6 +4115,20 @@ void perf_prepare_sample(struct perf_event_header *header,
4067 } 4115 }
4068 header->size += size; 4116 header->size += size;
4069 } 4117 }
4118
4119 if (sample_type & PERF_SAMPLE_REGS_USER) {
4120 /* regs dump ABI info */
4121 int size = sizeof(u64);
4122
4123 perf_sample_regs_user(&data->regs_user, regs);
4124
4125 if (data->regs_user.regs) {
4126 u64 mask = event->attr.sample_regs_user;
4127 size += hweight64(mask) * sizeof(u64);
4128 }
4129
4130 header->size += size;
4131 }
4070} 4132}
4071 4133
4072static void perf_event_output(struct perf_event *event, 4134static void perf_event_output(struct perf_event *event,
@@ -6142,6 +6204,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
6142 attr->branch_sample_type = mask; 6204 attr->branch_sample_type = mask;
6143 } 6205 }
6144 } 6206 }
6207
6208 if (attr->sample_type & PERF_SAMPLE_REGS_USER)
6209 ret = perf_reg_validate(attr->sample_regs_user);
6210
6145out: 6211out:
6146 return ret; 6212 return ret;
6147 6213