aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2012-02-09 17:20:51 -0500
committerIngo Molnar <mingo@elte.hu>2012-03-05 08:55:39 -0500
commitbce38cd53e5ddba9cb6d708c4ef3d04a4016ec7e (patch)
tree8b42880d3ff9f250e246eb31bd80e6c36c99d8e1 /kernel/events
parent737f24bda723fdf89ecaacb99fa2bf5683c32799 (diff)
perf: Add generic taken branch sampling support
This patch adds the ability to sample taken branches to the perf_event interface. The ability to capture taken branches is very useful for all sorts of analysis. For instance, basic block profiling, call counts, statistical call graph. This new capability requires hardware assist and as such may not be available on all HW platforms. On Intel x86 it is implemented on top of the Last Branch Record (LBR) facility. To enable taken branches sampling, the PERF_SAMPLE_BRANCH_STACK bit must be set in attr->sample_type. Sampled taken branches may be filtered by type and/or priv levels. The patch adds a new field, called branch_sample_type, to the perf_event_attr structure. It contains a bitmask of filters to apply to the sampled taken branches. Filters may be implemented in HW. If the HW filter does not exist or is not good enough, some arch may also implement a SW filter. The following generic filters are currently defined: - PERF_SAMPLE_USER only branches whose targets are at the user level - PERF_SAMPLE_KERNEL only branches whose targets are at the kernel level - PERF_SAMPLE_HV only branches whose targets are at the hypervisor level - PERF_SAMPLE_ANY any type of branches (subject to priv levels filters) - PERF_SAMPLE_ANY_CALL any call branches (may incl. syscall on some arch) - PERF_SAMPLE_ANY_RET any return branches (may incl. syscall returns on some arch) - PERF_SAMPLE_IND_CALL indirect call branches Obviously filter may be combined. The priv level bits are optional. If not provided, the priv level of the associated event are used. It is possible to collect branches at a priv level different from the associated event. Use of kernel, hv priv levels is subject to permissions and availability (hv). The number of taken branch records present in each sample may vary based on HW, the type of sampled branches, the executed code. Therefore each sample contains the number of taken branches it contains. Signed-off-by: Stephane Eranian <eranian@google.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1328826068-11713-2-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/events')
-rw-r--r--kernel/events/core.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index e8b32ac75ce3..5820efdf47cd 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -118,6 +118,13 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
118 PERF_FLAG_FD_OUTPUT |\ 118 PERF_FLAG_FD_OUTPUT |\
119 PERF_FLAG_PID_CGROUP) 119 PERF_FLAG_PID_CGROUP)
120 120
121/*
122 * branch priv levels that need permission checks
123 */
124#define PERF_SAMPLE_BRANCH_PERM_PLM \
125 (PERF_SAMPLE_BRANCH_KERNEL |\
126 PERF_SAMPLE_BRANCH_HV)
127
121enum event_type_t { 128enum event_type_t {
122 EVENT_FLEXIBLE = 0x1, 129 EVENT_FLEXIBLE = 0x1,
123 EVENT_PINNED = 0x2, 130 EVENT_PINNED = 0x2,
@@ -3907,6 +3914,24 @@ void perf_output_sample(struct perf_output_handle *handle,
3907 } 3914 }
3908 } 3915 }
3909 } 3916 }
3917
3918 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
3919 if (data->br_stack) {
3920 size_t size;
3921
3922 size = data->br_stack->nr
3923 * sizeof(struct perf_branch_entry);
3924
3925 perf_output_put(handle, data->br_stack->nr);
3926 perf_output_copy(handle, data->br_stack->entries, size);
3927 } else {
3928 /*
3929 * we always store at least the value of nr
3930 */
3931 u64 nr = 0;
3932 perf_output_put(handle, nr);
3933 }
3934 }
3910} 3935}
3911 3936
3912void perf_prepare_sample(struct perf_event_header *header, 3937void perf_prepare_sample(struct perf_event_header *header,
@@ -3949,6 +3974,15 @@ void perf_prepare_sample(struct perf_event_header *header,
3949 WARN_ON_ONCE(size & (sizeof(u64)-1)); 3974 WARN_ON_ONCE(size & (sizeof(u64)-1));
3950 header->size += size; 3975 header->size += size;
3951 } 3976 }
3977
3978 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
3979 int size = sizeof(u64); /* nr */
3980 if (data->br_stack) {
3981 size += data->br_stack->nr
3982 * sizeof(struct perf_branch_entry);
3983 }
3984 header->size += size;
3985 }
3952} 3986}
3953 3987
3954static void perf_event_output(struct perf_event *event, 3988static void perf_event_output(struct perf_event *event,
@@ -5935,6 +5969,40 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
5935 if (attr->read_format & ~(PERF_FORMAT_MAX-1)) 5969 if (attr->read_format & ~(PERF_FORMAT_MAX-1))
5936 return -EINVAL; 5970 return -EINVAL;
5937 5971
5972 if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) {
5973 u64 mask = attr->branch_sample_type;
5974
5975 /* only using defined bits */
5976 if (mask & ~(PERF_SAMPLE_BRANCH_MAX-1))
5977 return -EINVAL;
5978
5979 /* at least one branch bit must be set */
5980 if (!(mask & ~PERF_SAMPLE_BRANCH_PLM_ALL))
5981 return -EINVAL;
5982
5983 /* kernel level capture: check permissions */
5984 if ((mask & PERF_SAMPLE_BRANCH_PERM_PLM)
5985 && perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
5986 return -EACCES;
5987
5988 /* propagate priv level, when not set for branch */
5989 if (!(mask & PERF_SAMPLE_BRANCH_PLM_ALL)) {
5990
5991 /* exclude_kernel checked on syscall entry */
5992 if (!attr->exclude_kernel)
5993 mask |= PERF_SAMPLE_BRANCH_KERNEL;
5994
5995 if (!attr->exclude_user)
5996 mask |= PERF_SAMPLE_BRANCH_USER;
5997
5998 if (!attr->exclude_hv)
5999 mask |= PERF_SAMPLE_BRANCH_HV;
6000 /*
6001 * adjust user setting (for HW filter setup)
6002 */
6003 attr->branch_sample_type = mask;
6004 }
6005 }
5938out: 6006out:
5939 return ret; 6007 return ret;
5940 6008