diff options
author | Li Zefan <lizf@cn.fujitsu.com> | 2009-10-14 23:21:42 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-10-15 05:35:23 -0400 |
commit | 6fb2915df7f0747d9044da9dbff5b46dc2e20830 (patch) | |
tree | b4c5d7d913362ae6161c2859a7a385263330e965 /kernel/perf_event.c | |
parent | b0f1a59a98d7ac2102e7e4f22904c26d564a5628 (diff) |
tracing/profile: Add filter support
- Add an ioctl to allocate a filter for a perf event.
- Free the filter when the associated perf event is to be freed.
- Do the filtering in perf_swevent_match().
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <4AD69546.8050401@cn.fujitsu.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 80 |
1 files changed, 73 insertions, 7 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 9d0b5c665883..12b5ec39bf97 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/anon_inodes.h> | 28 | #include <linux/anon_inodes.h> |
29 | #include <linux/kernel_stat.h> | 29 | #include <linux/kernel_stat.h> |
30 | #include <linux/perf_event.h> | 30 | #include <linux/perf_event.h> |
31 | #include <linux/ftrace_event.h> | ||
31 | 32 | ||
32 | #include <asm/irq_regs.h> | 33 | #include <asm/irq_regs.h> |
33 | 34 | ||
@@ -1658,6 +1659,8 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu) | |||
1658 | return ERR_PTR(err); | 1659 | return ERR_PTR(err); |
1659 | } | 1660 | } |
1660 | 1661 | ||
1662 | static void perf_event_free_filter(struct perf_event *event); | ||
1663 | |||
1661 | static void free_event_rcu(struct rcu_head *head) | 1664 | static void free_event_rcu(struct rcu_head *head) |
1662 | { | 1665 | { |
1663 | struct perf_event *event; | 1666 | struct perf_event *event; |
@@ -1665,6 +1668,7 @@ static void free_event_rcu(struct rcu_head *head) | |||
1665 | event = container_of(head, struct perf_event, rcu_head); | 1668 | event = container_of(head, struct perf_event, rcu_head); |
1666 | if (event->ns) | 1669 | if (event->ns) |
1667 | put_pid_ns(event->ns); | 1670 | put_pid_ns(event->ns); |
1671 | perf_event_free_filter(event); | ||
1668 | kfree(event); | 1672 | kfree(event); |
1669 | } | 1673 | } |
1670 | 1674 | ||
@@ -1974,7 +1978,8 @@ unlock: | |||
1974 | return ret; | 1978 | return ret; |
1975 | } | 1979 | } |
1976 | 1980 | ||
1977 | int perf_event_set_output(struct perf_event *event, int output_fd); | 1981 | static int perf_event_set_output(struct perf_event *event, int output_fd); |
1982 | static int perf_event_set_filter(struct perf_event *event, void __user *arg); | ||
1978 | 1983 | ||
1979 | static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1984 | static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1980 | { | 1985 | { |
@@ -2002,6 +2007,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
2002 | case PERF_EVENT_IOC_SET_OUTPUT: | 2007 | case PERF_EVENT_IOC_SET_OUTPUT: |
2003 | return perf_event_set_output(event, arg); | 2008 | return perf_event_set_output(event, arg); |
2004 | 2009 | ||
2010 | case PERF_EVENT_IOC_SET_FILTER: | ||
2011 | return perf_event_set_filter(event, (void __user *)arg); | ||
2012 | |||
2005 | default: | 2013 | default: |
2006 | return -ENOTTY; | 2014 | return -ENOTTY; |
2007 | } | 2015 | } |
@@ -3806,9 +3814,14 @@ static int perf_swevent_is_counting(struct perf_event *event) | |||
3806 | return 1; | 3814 | return 1; |
3807 | } | 3815 | } |
3808 | 3816 | ||
3817 | static int perf_tp_event_match(struct perf_event *event, | ||
3818 | struct perf_sample_data *data); | ||
3819 | |||
3809 | static int perf_swevent_match(struct perf_event *event, | 3820 | static int perf_swevent_match(struct perf_event *event, |
3810 | enum perf_type_id type, | 3821 | enum perf_type_id type, |
3811 | u32 event_id, struct pt_regs *regs) | 3822 | u32 event_id, |
3823 | struct perf_sample_data *data, | ||
3824 | struct pt_regs *regs) | ||
3812 | { | 3825 | { |
3813 | if (!perf_swevent_is_counting(event)) | 3826 | if (!perf_swevent_is_counting(event)) |
3814 | return 0; | 3827 | return 0; |
@@ -3826,6 +3839,10 @@ static int perf_swevent_match(struct perf_event *event, | |||
3826 | return 0; | 3839 | return 0; |
3827 | } | 3840 | } |
3828 | 3841 | ||
3842 | if (event->attr.type == PERF_TYPE_TRACEPOINT && | ||
3843 | !perf_tp_event_match(event, data)) | ||
3844 | return 0; | ||
3845 | |||
3829 | return 1; | 3846 | return 1; |
3830 | } | 3847 | } |
3831 | 3848 | ||
@@ -3842,7 +3859,7 @@ static void perf_swevent_ctx_event(struct perf_event_context *ctx, | |||
3842 | 3859 | ||
3843 | rcu_read_lock(); | 3860 | rcu_read_lock(); |
3844 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { | 3861 | list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { |
3845 | if (perf_swevent_match(event, type, event_id, regs)) | 3862 | if (perf_swevent_match(event, type, event_id, data, regs)) |
3846 | perf_swevent_add(event, nr, nmi, data, regs); | 3863 | perf_swevent_add(event, nr, nmi, data, regs); |
3847 | } | 3864 | } |
3848 | rcu_read_unlock(); | 3865 | rcu_read_unlock(); |
@@ -4086,6 +4103,7 @@ static const struct pmu perf_ops_task_clock = { | |||
4086 | }; | 4103 | }; |
4087 | 4104 | ||
4088 | #ifdef CONFIG_EVENT_PROFILE | 4105 | #ifdef CONFIG_EVENT_PROFILE |
4106 | |||
4089 | void perf_tp_event(int event_id, u64 addr, u64 count, void *record, | 4107 | void perf_tp_event(int event_id, u64 addr, u64 count, void *record, |
4090 | int entry_size) | 4108 | int entry_size) |
4091 | { | 4109 | { |
@@ -4109,8 +4127,15 @@ void perf_tp_event(int event_id, u64 addr, u64 count, void *record, | |||
4109 | } | 4127 | } |
4110 | EXPORT_SYMBOL_GPL(perf_tp_event); | 4128 | EXPORT_SYMBOL_GPL(perf_tp_event); |
4111 | 4129 | ||
4112 | extern int ftrace_profile_enable(int); | 4130 | static int perf_tp_event_match(struct perf_event *event, |
4113 | extern void ftrace_profile_disable(int); | 4131 | struct perf_sample_data *data) |
4132 | { | ||
4133 | void *record = data->raw->data; | ||
4134 | |||
4135 | if (likely(!event->filter) || filter_match_preds(event->filter, record)) | ||
4136 | return 1; | ||
4137 | return 0; | ||
4138 | } | ||
4114 | 4139 | ||
4115 | static void tp_perf_event_destroy(struct perf_event *event) | 4140 | static void tp_perf_event_destroy(struct perf_event *event) |
4116 | { | 4141 | { |
@@ -4135,12 +4160,53 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event) | |||
4135 | 4160 | ||
4136 | return &perf_ops_generic; | 4161 | return &perf_ops_generic; |
4137 | } | 4162 | } |
4163 | |||
4164 | static int perf_event_set_filter(struct perf_event *event, void __user *arg) | ||
4165 | { | ||
4166 | char *filter_str; | ||
4167 | int ret; | ||
4168 | |||
4169 | if (event->attr.type != PERF_TYPE_TRACEPOINT) | ||
4170 | return -EINVAL; | ||
4171 | |||
4172 | filter_str = strndup_user(arg, PAGE_SIZE); | ||
4173 | if (IS_ERR(filter_str)) | ||
4174 | return PTR_ERR(filter_str); | ||
4175 | |||
4176 | ret = ftrace_profile_set_filter(event, event->attr.config, filter_str); | ||
4177 | |||
4178 | kfree(filter_str); | ||
4179 | return ret; | ||
4180 | } | ||
4181 | |||
4182 | static void perf_event_free_filter(struct perf_event *event) | ||
4183 | { | ||
4184 | ftrace_profile_free_filter(event); | ||
4185 | } | ||
4186 | |||
4138 | #else | 4187 | #else |
4188 | |||
4189 | static int perf_tp_event_match(struct perf_event *event, | ||
4190 | struct perf_sample_data *data) | ||
4191 | { | ||
4192 | return 1; | ||
4193 | } | ||
4194 | |||
4139 | static const struct pmu *tp_perf_event_init(struct perf_event *event) | 4195 | static const struct pmu *tp_perf_event_init(struct perf_event *event) |
4140 | { | 4196 | { |
4141 | return NULL; | 4197 | return NULL; |
4142 | } | 4198 | } |
4143 | #endif | 4199 | |
4200 | static int perf_event_set_filter(struct perf_event *event, void __user *arg) | ||
4201 | { | ||
4202 | return -ENOENT; | ||
4203 | } | ||
4204 | |||
4205 | static void perf_event_free_filter(struct perf_event *event) | ||
4206 | { | ||
4207 | } | ||
4208 | |||
4209 | #endif /* CONFIG_EVENT_PROFILE */ | ||
4144 | 4210 | ||
4145 | atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; | 4211 | atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; |
4146 | 4212 | ||
@@ -4394,7 +4460,7 @@ err_size: | |||
4394 | goto out; | 4460 | goto out; |
4395 | } | 4461 | } |
4396 | 4462 | ||
4397 | int perf_event_set_output(struct perf_event *event, int output_fd) | 4463 | static int perf_event_set_output(struct perf_event *event, int output_fd) |
4398 | { | 4464 | { |
4399 | struct perf_event *output_event = NULL; | 4465 | struct perf_event *output_event = NULL; |
4400 | struct file *output_file = NULL; | 4466 | struct file *output_file = NULL; |