aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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