diff options
author | Stephane Eranian <eranian@google.com> | 2014-09-24 07:48:37 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-11-16 05:41:57 -0500 |
commit | 60e2364e60e86e81bc6377f49779779e6120977f (patch) | |
tree | 669f675c67744cbdd61291f2ec3d03cbb51288d3 | |
parent | af4bdcf675cf768dc3558682fc178e0e3e70bb52 (diff) |
perf: Add ability to sample machine state on interrupt
Enable capture of interrupted machine state for each sample.
Registers to sample are passed per event in the sample_regs_intr bitmask.
To sample interrupt machine state, the PERF_SAMPLE_INTR_REGS must be passed in
sample_type.
The list of available registers is arch dependent and provided by asm/perf_regs.h
Registers are laid out as u64 in the order of the bit order of sample_intr_regs.
This patch also adds a new ABI version PERF_ATTR_SIZE_VER4 because we extend
the perf_event_attr struct with a new u64 field.
Reviewed-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Stephane Eranian <eranian@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: cebbert.lkml@gmail.com
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-api@vger.kernel.org
Link: http://lkml.kernel.org/r/1411559322-16548-2-git-send-email-eranian@google.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/perf_event.h | 7 | ||||
-rw-r--r-- | include/uapi/linux/perf_event.h | 15 | ||||
-rw-r--r-- | kernel/events/core.c | 46 |
3 files changed, 63 insertions, 5 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 893a0d07986f..68d46d536e24 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -79,7 +79,7 @@ struct perf_branch_stack { | |||
79 | struct perf_branch_entry entries[0]; | 79 | struct perf_branch_entry entries[0]; |
80 | }; | 80 | }; |
81 | 81 | ||
82 | struct perf_regs_user { | 82 | struct perf_regs { |
83 | __u64 abi; | 83 | __u64 abi; |
84 | struct pt_regs *regs; | 84 | struct pt_regs *regs; |
85 | }; | 85 | }; |
@@ -600,7 +600,8 @@ struct perf_sample_data { | |||
600 | struct perf_callchain_entry *callchain; | 600 | struct perf_callchain_entry *callchain; |
601 | struct perf_raw_record *raw; | 601 | struct perf_raw_record *raw; |
602 | struct perf_branch_stack *br_stack; | 602 | struct perf_branch_stack *br_stack; |
603 | struct perf_regs_user regs_user; | 603 | struct perf_regs regs_user; |
604 | struct perf_regs regs_intr; | ||
604 | u64 stack_user_size; | 605 | u64 stack_user_size; |
605 | u64 weight; | 606 | u64 weight; |
606 | /* | 607 | /* |
@@ -630,6 +631,8 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, | |||
630 | data->weight = 0; | 631 | data->weight = 0; |
631 | data->data_src.val = PERF_MEM_NA; | 632 | data->data_src.val = PERF_MEM_NA; |
632 | data->txn = 0; | 633 | data->txn = 0; |
634 | data->regs_intr.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
635 | data->regs_intr.regs = NULL; | ||
633 | } | 636 | } |
634 | 637 | ||
635 | extern void perf_output_sample(struct perf_output_handle *handle, | 638 | extern void perf_output_sample(struct perf_output_handle *handle, |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 9d845404d875..9b79abbd1ab8 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -137,8 +137,9 @@ enum perf_event_sample_format { | |||
137 | PERF_SAMPLE_DATA_SRC = 1U << 15, | 137 | PERF_SAMPLE_DATA_SRC = 1U << 15, |
138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, | 138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, |
139 | PERF_SAMPLE_TRANSACTION = 1U << 17, | 139 | PERF_SAMPLE_TRANSACTION = 1U << 17, |
140 | PERF_SAMPLE_REGS_INTR = 1U << 18, | ||
140 | 141 | ||
141 | PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */ | 142 | PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */ |
142 | }; | 143 | }; |
143 | 144 | ||
144 | /* | 145 | /* |
@@ -238,6 +239,7 @@ enum perf_event_read_format { | |||
238 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | 239 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ |
239 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ | 240 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ |
240 | /* add: sample_stack_user */ | 241 | /* add: sample_stack_user */ |
242 | #define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */ | ||
241 | 243 | ||
242 | /* | 244 | /* |
243 | * Hardware event_id to monitor via a performance monitoring event: | 245 | * Hardware event_id to monitor via a performance monitoring event: |
@@ -334,6 +336,15 @@ struct perf_event_attr { | |||
334 | 336 | ||
335 | /* Align to u64. */ | 337 | /* Align to u64. */ |
336 | __u32 __reserved_2; | 338 | __u32 __reserved_2; |
339 | /* | ||
340 | * Defines set of regs to dump for each sample | ||
341 | * state captured on: | ||
342 | * - precise = 0: PMU interrupt | ||
343 | * - precise > 0: sampled instruction | ||
344 | * | ||
345 | * See asm/perf_regs.h for details. | ||
346 | */ | ||
347 | __u64 sample_regs_intr; | ||
337 | }; | 348 | }; |
338 | 349 | ||
339 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) | 350 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) |
@@ -686,6 +697,8 @@ enum perf_event_type { | |||
686 | * { u64 weight; } && PERF_SAMPLE_WEIGHT | 697 | * { u64 weight; } && PERF_SAMPLE_WEIGHT |
687 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC | 698 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC |
688 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION | 699 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION |
700 | * { u64 abi; # enum perf_sample_regs_abi | ||
701 | * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR | ||
689 | * }; | 702 | * }; |
690 | */ | 703 | */ |
691 | PERF_RECORD_SAMPLE = 9, | 704 | PERF_RECORD_SAMPLE = 9, |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 1cd5eef1fcdd..c2be1597ece7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -4460,7 +4460,7 @@ perf_output_sample_regs(struct perf_output_handle *handle, | |||
4460 | } | 4460 | } |
4461 | } | 4461 | } |
4462 | 4462 | ||
4463 | static void perf_sample_regs_user(struct perf_regs_user *regs_user, | 4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, |
4464 | struct pt_regs *regs) | 4464 | struct pt_regs *regs) |
4465 | { | 4465 | { |
4466 | if (!user_mode(regs)) { | 4466 | if (!user_mode(regs)) { |
@@ -4476,6 +4476,14 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user, | |||
4476 | } | 4476 | } |
4477 | } | 4477 | } |
4478 | 4478 | ||
4479 | static void perf_sample_regs_intr(struct perf_regs *regs_intr, | ||
4480 | struct pt_regs *regs) | ||
4481 | { | ||
4482 | regs_intr->regs = regs; | ||
4483 | regs_intr->abi = perf_reg_abi(current); | ||
4484 | } | ||
4485 | |||
4486 | |||
4479 | /* | 4487 | /* |
4480 | * Get remaining task size from user stack pointer. | 4488 | * Get remaining task size from user stack pointer. |
4481 | * | 4489 | * |
@@ -4857,6 +4865,23 @@ void perf_output_sample(struct perf_output_handle *handle, | |||
4857 | if (sample_type & PERF_SAMPLE_TRANSACTION) | 4865 | if (sample_type & PERF_SAMPLE_TRANSACTION) |
4858 | perf_output_put(handle, data->txn); | 4866 | perf_output_put(handle, data->txn); |
4859 | 4867 | ||
4868 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
4869 | u64 abi = data->regs_intr.abi; | ||
4870 | /* | ||
4871 | * If there are no regs to dump, notice it through | ||
4872 | * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). | ||
4873 | */ | ||
4874 | perf_output_put(handle, abi); | ||
4875 | |||
4876 | if (abi) { | ||
4877 | u64 mask = event->attr.sample_regs_intr; | ||
4878 | |||
4879 | perf_output_sample_regs(handle, | ||
4880 | data->regs_intr.regs, | ||
4881 | mask); | ||
4882 | } | ||
4883 | } | ||
4884 | |||
4860 | if (!event->attr.watermark) { | 4885 | if (!event->attr.watermark) { |
4861 | int wakeup_events = event->attr.wakeup_events; | 4886 | int wakeup_events = event->attr.wakeup_events; |
4862 | 4887 | ||
@@ -4943,7 +4968,7 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
4943 | * in case new sample type is added, because we could eat | 4968 | * in case new sample type is added, because we could eat |
4944 | * up the rest of the sample size. | 4969 | * up the rest of the sample size. |
4945 | */ | 4970 | */ |
4946 | struct perf_regs_user *uregs = &data->regs_user; | 4971 | struct perf_regs *uregs = &data->regs_user; |
4947 | u16 stack_size = event->attr.sample_stack_user; | 4972 | u16 stack_size = event->attr.sample_stack_user; |
4948 | u16 size = sizeof(u64); | 4973 | u16 size = sizeof(u64); |
4949 | 4974 | ||
@@ -4964,6 +4989,21 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
4964 | data->stack_user_size = stack_size; | 4989 | data->stack_user_size = stack_size; |
4965 | header->size += size; | 4990 | header->size += size; |
4966 | } | 4991 | } |
4992 | |||
4993 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
4994 | /* regs dump ABI info */ | ||
4995 | int size = sizeof(u64); | ||
4996 | |||
4997 | perf_sample_regs_intr(&data->regs_intr, regs); | ||
4998 | |||
4999 | if (data->regs_intr.regs) { | ||
5000 | u64 mask = event->attr.sample_regs_intr; | ||
5001 | |||
5002 | size += hweight64(mask) * sizeof(u64); | ||
5003 | } | ||
5004 | |||
5005 | header->size += size; | ||
5006 | } | ||
4967 | } | 5007 | } |
4968 | 5008 | ||
4969 | static void perf_event_output(struct perf_event *event, | 5009 | static void perf_event_output(struct perf_event *event, |
@@ -7151,6 +7191,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, | |||
7151 | ret = -EINVAL; | 7191 | ret = -EINVAL; |
7152 | } | 7192 | } |
7153 | 7193 | ||
7194 | if (attr->sample_type & PERF_SAMPLE_REGS_INTR) | ||
7195 | ret = perf_reg_validate(attr->sample_regs_intr); | ||
7154 | out: | 7196 | out: |
7155 | return ret; | 7197 | return ret; |
7156 | 7198 | ||