diff options
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/trace.h | 28 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 77 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 326 | ||||
-rw-r--r-- | kernel/trace/trace_events_stage_3.h | 4 |
5 files changed, 436 insertions, 0 deletions
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 0e45c206c2f9..2630f5121ec1 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -45,5 +45,6 @@ obj-$(CONFIG_EVENT_TRACER) += events.o | |||
45 | obj-$(CONFIG_EVENT_TRACER) += trace_export.o | 45 | obj-$(CONFIG_EVENT_TRACER) += trace_export.o |
46 | obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o | 46 | obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o |
47 | obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o | 47 | obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o |
48 | obj-$(CONFIG_EVENT_TRACER) += trace_events_filter.o | ||
48 | 49 | ||
49 | libftrace-y := ftrace.o | 50 | libftrace-y := ftrace.o |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 9288dc7ad14f..d9eb39e4bb38 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -795,6 +795,7 @@ struct ftrace_event_call { | |||
795 | int (*show_format)(struct trace_seq *s); | 795 | int (*show_format)(struct trace_seq *s); |
796 | int (*define_fields)(void); | 796 | int (*define_fields)(void); |
797 | struct list_head fields; | 797 | struct list_head fields; |
798 | struct filter_pred **preds; | ||
798 | 799 | ||
799 | #ifdef CONFIG_EVENT_PROFILE | 800 | #ifdef CONFIG_EVENT_PROFILE |
800 | atomic_t profile_count; | 801 | atomic_t profile_count; |
@@ -803,8 +804,35 @@ struct ftrace_event_call { | |||
803 | #endif | 804 | #endif |
804 | }; | 805 | }; |
805 | 806 | ||
807 | #define MAX_FILTER_PRED 8 | ||
808 | |||
809 | struct filter_pred; | ||
810 | |||
811 | typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event); | ||
812 | |||
813 | struct filter_pred { | ||
814 | filter_pred_fn_t fn; | ||
815 | u64 val; | ||
816 | char *str_val; | ||
817 | int str_len; | ||
818 | char *field_name; | ||
819 | int offset; | ||
820 | int not; | ||
821 | int or; | ||
822 | int compound; | ||
823 | int clear; | ||
824 | }; | ||
825 | |||
806 | int trace_define_field(struct ftrace_event_call *call, char *type, | 826 | int trace_define_field(struct ftrace_event_call *call, char *type, |
807 | char *name, int offset, int size); | 827 | char *name, int offset, int size); |
828 | extern void filter_free_pred(struct filter_pred *pred); | ||
829 | extern int filter_print_preds(struct filter_pred **preds, char *buf); | ||
830 | extern int filter_parse(char **pbuf, struct filter_pred *pred); | ||
831 | extern int filter_add_pred(struct ftrace_event_call *call, | ||
832 | struct filter_pred *pred); | ||
833 | extern void filter_free_preds(struct ftrace_event_call *call); | ||
834 | extern int filter_match_preds(struct ftrace_event_call *call, void *rec); | ||
835 | |||
808 | void event_trace_printk(unsigned long ip, const char *fmt, ...); | 836 | void event_trace_printk(unsigned long ip, const char *fmt, ...); |
809 | extern struct ftrace_event_call __start_ftrace_events[]; | 837 | extern struct ftrace_event_call __start_ftrace_events[]; |
810 | extern struct ftrace_event_call __stop_ftrace_events[]; | 838 | extern struct ftrace_event_call __stop_ftrace_events[]; |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 961b057da28b..97470c48956e 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -459,6 +459,71 @@ event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |||
459 | return r; | 459 | return r; |
460 | } | 460 | } |
461 | 461 | ||
462 | static ssize_t | ||
463 | event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
464 | loff_t *ppos) | ||
465 | { | ||
466 | struct ftrace_event_call *call = filp->private_data; | ||
467 | struct trace_seq *s; | ||
468 | int r; | ||
469 | |||
470 | if (*ppos) | ||
471 | return 0; | ||
472 | |||
473 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
474 | if (!s) | ||
475 | return -ENOMEM; | ||
476 | |||
477 | trace_seq_init(s); | ||
478 | |||
479 | r = filter_print_preds(call->preds, s->buffer); | ||
480 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, r); | ||
481 | |||
482 | kfree(s); | ||
483 | |||
484 | return r; | ||
485 | } | ||
486 | |||
487 | static ssize_t | ||
488 | event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
489 | loff_t *ppos) | ||
490 | { | ||
491 | struct ftrace_event_call *call = filp->private_data; | ||
492 | char buf[64], *pbuf = buf; | ||
493 | struct filter_pred *pred; | ||
494 | int err; | ||
495 | |||
496 | if (cnt >= sizeof(buf)) | ||
497 | return -EINVAL; | ||
498 | |||
499 | if (copy_from_user(&buf, ubuf, cnt)) | ||
500 | return -EFAULT; | ||
501 | |||
502 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | ||
503 | if (!pred) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | err = filter_parse(&pbuf, pred); | ||
507 | if (err < 0) { | ||
508 | filter_free_pred(pred); | ||
509 | return err; | ||
510 | } | ||
511 | |||
512 | if (pred->clear) { | ||
513 | filter_free_preds(call); | ||
514 | return cnt; | ||
515 | } | ||
516 | |||
517 | if (filter_add_pred(call, pred)) { | ||
518 | filter_free_pred(pred); | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | |||
522 | *ppos += cnt; | ||
523 | |||
524 | return cnt; | ||
525 | } | ||
526 | |||
462 | static const struct seq_operations show_event_seq_ops = { | 527 | static const struct seq_operations show_event_seq_ops = { |
463 | .start = t_start, | 528 | .start = t_start, |
464 | .next = t_next, | 529 | .next = t_next, |
@@ -504,6 +569,12 @@ static const struct file_operations ftrace_event_id_fops = { | |||
504 | .read = event_id_read, | 569 | .read = event_id_read, |
505 | }; | 570 | }; |
506 | 571 | ||
572 | static const struct file_operations ftrace_event_filter_fops = { | ||
573 | .open = tracing_open_generic, | ||
574 | .read = event_filter_read, | ||
575 | .write = event_filter_write, | ||
576 | }; | ||
577 | |||
507 | static struct dentry *event_trace_events_dir(void) | 578 | static struct dentry *event_trace_events_dir(void) |
508 | { | 579 | { |
509 | static struct dentry *d_tracer; | 580 | static struct dentry *d_tracer; |
@@ -619,6 +690,12 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
619 | } | 690 | } |
620 | } | 691 | } |
621 | 692 | ||
693 | entry = debugfs_create_file("filter", 0444, call->dir, call, | ||
694 | &ftrace_event_filter_fops); | ||
695 | if (!entry) | ||
696 | pr_warning("Could not create debugfs " | ||
697 | "'%s/filter' entry\n", call->name); | ||
698 | |||
622 | /* A trace may not want to export its format */ | 699 | /* A trace may not want to export its format */ |
623 | if (!call->show_format) | 700 | if (!call->show_format) |
624 | return 0; | 701 | return 0; |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c new file mode 100644 index 000000000000..8e8c5fa25be9 --- /dev/null +++ b/kernel/trace/trace_events_filter.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | * trace_events_filter - generic event filtering | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
19 | */ | ||
20 | |||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/ctype.h> | ||
25 | |||
26 | #include "trace.h" | ||
27 | |||
28 | static int filter_pred_64(struct filter_pred *pred, void *event) | ||
29 | { | ||
30 | u64 *addr = (u64 *)(event + pred->offset); | ||
31 | u64 val = (u64)pred->val; | ||
32 | int match; | ||
33 | |||
34 | match = (val == *addr) ^ pred->not; | ||
35 | |||
36 | return match; | ||
37 | } | ||
38 | |||
39 | static int filter_pred_32(struct filter_pred *pred, void *event) | ||
40 | { | ||
41 | u32 *addr = (u32 *)(event + pred->offset); | ||
42 | u32 val = (u32)pred->val; | ||
43 | int match; | ||
44 | |||
45 | match = (val == *addr) ^ pred->not; | ||
46 | |||
47 | return match; | ||
48 | } | ||
49 | |||
50 | static int filter_pred_16(struct filter_pred *pred, void *event) | ||
51 | { | ||
52 | u16 *addr = (u16 *)(event + pred->offset); | ||
53 | u16 val = (u16)pred->val; | ||
54 | int match; | ||
55 | |||
56 | match = (val == *addr) ^ pred->not; | ||
57 | |||
58 | return match; | ||
59 | } | ||
60 | |||
61 | static int filter_pred_8(struct filter_pred *pred, void *event) | ||
62 | { | ||
63 | u8 *addr = (u8 *)(event + pred->offset); | ||
64 | u8 val = (u8)pred->val; | ||
65 | int match; | ||
66 | |||
67 | match = (val == *addr) ^ pred->not; | ||
68 | |||
69 | return match; | ||
70 | } | ||
71 | |||
72 | static int filter_pred_string(struct filter_pred *pred, void *event) | ||
73 | { | ||
74 | char *addr = (char *)(event + pred->offset); | ||
75 | int cmp, match; | ||
76 | |||
77 | cmp = strncmp(addr, pred->str_val, pred->str_len); | ||
78 | |||
79 | match = (!cmp) ^ pred->not; | ||
80 | |||
81 | return match; | ||
82 | } | ||
83 | |||
84 | /* return 1 if event matches, 0 otherwise (discard) */ | ||
85 | int filter_match_preds(struct ftrace_event_call *call, void *rec) | ||
86 | { | ||
87 | int i, matched, and_failed = 0; | ||
88 | struct filter_pred *pred; | ||
89 | |||
90 | for (i = 0; i < MAX_FILTER_PRED; i++) { | ||
91 | if (call->preds[i]) { | ||
92 | pred = call->preds[i]; | ||
93 | if (and_failed && !pred->or) | ||
94 | continue; | ||
95 | matched = pred->fn(pred, rec); | ||
96 | if (!matched && !pred->or) { | ||
97 | and_failed = 1; | ||
98 | continue; | ||
99 | } else if (matched && pred->or) | ||
100 | return 1; | ||
101 | } else | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | if (and_failed) | ||
106 | return 0; | ||
107 | |||
108 | return 1; | ||
109 | } | ||
110 | |||
111 | int filter_print_preds(struct filter_pred **preds, char *buf) | ||
112 | { | ||
113 | ssize_t this_len = 0; | ||
114 | char *field_name; | ||
115 | struct filter_pred *pred; | ||
116 | int i; | ||
117 | |||
118 | if (!preds) { | ||
119 | this_len += sprintf(buf + this_len, "none\n"); | ||
120 | return this_len; | ||
121 | } | ||
122 | |||
123 | for (i = 0; i < MAX_FILTER_PRED; i++) { | ||
124 | if (preds[i]) { | ||
125 | pred = preds[i]; | ||
126 | field_name = pred->field_name; | ||
127 | if (i) | ||
128 | this_len += sprintf(buf + this_len, | ||
129 | pred->or ? "|| " : "&& "); | ||
130 | this_len += sprintf(buf + this_len, | ||
131 | "%s ", field_name); | ||
132 | this_len += sprintf(buf + this_len, | ||
133 | pred->not ? "!= " : "== "); | ||
134 | if (pred->str_val) | ||
135 | this_len += sprintf(buf + this_len, | ||
136 | "%s\n", pred->str_val); | ||
137 | else | ||
138 | this_len += sprintf(buf + this_len, | ||
139 | "%llu\n", pred->val); | ||
140 | } else | ||
141 | break; | ||
142 | } | ||
143 | |||
144 | return this_len; | ||
145 | } | ||
146 | |||
147 | static struct ftrace_event_field * | ||
148 | find_event_field(struct ftrace_event_call *call, char *name) | ||
149 | { | ||
150 | struct ftrace_event_field *field; | ||
151 | struct list_head *entry, *tmp; | ||
152 | |||
153 | list_for_each_safe(entry, tmp, &call->fields) { | ||
154 | field = list_entry(entry, struct ftrace_event_field, link); | ||
155 | if (!strcmp(field->name, name)) | ||
156 | return field; | ||
157 | } | ||
158 | |||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | void filter_free_pred(struct filter_pred *pred) | ||
163 | { | ||
164 | if (!pred) | ||
165 | return; | ||
166 | |||
167 | kfree(pred->field_name); | ||
168 | kfree(pred->str_val); | ||
169 | kfree(pred); | ||
170 | } | ||
171 | |||
172 | void filter_free_preds(struct ftrace_event_call *call) | ||
173 | { | ||
174 | int i; | ||
175 | |||
176 | if (call->preds) { | ||
177 | for (i = 0; i < MAX_FILTER_PRED; i++) | ||
178 | filter_free_pred(call->preds[i]); | ||
179 | kfree(call->preds); | ||
180 | call->preds = NULL; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static int __filter_add_pred(struct ftrace_event_call *call, | ||
185 | struct filter_pred *pred) | ||
186 | { | ||
187 | int i; | ||
188 | |||
189 | if (call->preds && !pred->compound) | ||
190 | filter_free_preds(call); | ||
191 | |||
192 | if (!call->preds) { | ||
193 | call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), | ||
194 | GFP_KERNEL); | ||
195 | if (!call->preds) | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | |||
199 | for (i = 0; i < MAX_FILTER_PRED; i++) { | ||
200 | if (!call->preds[i]) { | ||
201 | call->preds[i] = pred; | ||
202 | return 0; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | return -ENOMEM; | ||
207 | } | ||
208 | |||
209 | static int is_string_field(const char *type) | ||
210 | { | ||
211 | if (strchr(type, '[') && strstr(type, "char")) | ||
212 | return 1; | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | ||
218 | { | ||
219 | struct ftrace_event_field *field; | ||
220 | |||
221 | field = find_event_field(call, pred->field_name); | ||
222 | if (!field) | ||
223 | return -EINVAL; | ||
224 | |||
225 | pred->offset = field->offset; | ||
226 | |||
227 | if (is_string_field(field->type)) { | ||
228 | pred->fn = filter_pred_string; | ||
229 | pred->str_len = field->size; | ||
230 | return __filter_add_pred(call, pred); | ||
231 | } | ||
232 | |||
233 | switch (field->size) { | ||
234 | case 8: | ||
235 | pred->fn = filter_pred_64; | ||
236 | break; | ||
237 | case 4: | ||
238 | pred->fn = filter_pred_32; | ||
239 | break; | ||
240 | case 2: | ||
241 | pred->fn = filter_pred_16; | ||
242 | break; | ||
243 | case 1: | ||
244 | pred->fn = filter_pred_8; | ||
245 | break; | ||
246 | default: | ||
247 | return -EINVAL; | ||
248 | } | ||
249 | |||
250 | return __filter_add_pred(call, pred); | ||
251 | } | ||
252 | |||
253 | int filter_parse(char **pbuf, struct filter_pred *pred) | ||
254 | { | ||
255 | char *tmp, *tok, *val_str = NULL; | ||
256 | int tok_n = 0; | ||
257 | |||
258 | /* field ==/!= number, or/and field ==/!= number, number */ | ||
259 | while ((tok = strsep(pbuf, " \n"))) { | ||
260 | if (tok_n == 0) { | ||
261 | if (!strcmp(tok, "0")) { | ||
262 | pred->clear = 1; | ||
263 | return 0; | ||
264 | } else if (!strcmp(tok, "&&")) { | ||
265 | pred->or = 0; | ||
266 | pred->compound = 1; | ||
267 | } else if (!strcmp(tok, "||")) { | ||
268 | pred->or = 1; | ||
269 | pred->compound = 1; | ||
270 | } else | ||
271 | pred->field_name = tok; | ||
272 | tok_n = 1; | ||
273 | continue; | ||
274 | } | ||
275 | if (tok_n == 1) { | ||
276 | if (!pred->field_name) | ||
277 | pred->field_name = tok; | ||
278 | else if (!strcmp(tok, "!=")) | ||
279 | pred->not = 1; | ||
280 | else if (!strcmp(tok, "==")) | ||
281 | pred->not = 0; | ||
282 | else { | ||
283 | pred->field_name = NULL; | ||
284 | return -EINVAL; | ||
285 | } | ||
286 | tok_n = 2; | ||
287 | continue; | ||
288 | } | ||
289 | if (tok_n == 2) { | ||
290 | if (pred->compound) { | ||
291 | if (!strcmp(tok, "!=")) | ||
292 | pred->not = 1; | ||
293 | else if (!strcmp(tok, "==")) | ||
294 | pred->not = 0; | ||
295 | else { | ||
296 | pred->field_name = NULL; | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | } else { | ||
300 | val_str = tok; | ||
301 | break; /* done */ | ||
302 | } | ||
303 | tok_n = 3; | ||
304 | continue; | ||
305 | } | ||
306 | if (tok_n == 3) { | ||
307 | val_str = tok; | ||
308 | break; /* done */ | ||
309 | } | ||
310 | } | ||
311 | |||
312 | pred->field_name = kstrdup(pred->field_name, GFP_KERNEL); | ||
313 | if (!pred->field_name) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | pred->val = simple_strtoull(val_str, &tmp, 10); | ||
317 | if (tmp == val_str) { | ||
318 | pred->str_val = kstrdup(val_str, GFP_KERNEL); | ||
319 | if (!pred->str_val) | ||
320 | return -ENOMEM; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | |||
diff --git a/kernel/trace/trace_events_stage_3.h b/kernel/trace/trace_events_stage_3.h index 468938f70141..ebf215e87d5e 100644 --- a/kernel/trace/trace_events_stage_3.h +++ b/kernel/trace/trace_events_stage_3.h | |||
@@ -204,6 +204,7 @@ static struct ftrace_event_call event_##call; \ | |||
204 | \ | 204 | \ |
205 | static void ftrace_raw_event_##call(proto) \ | 205 | static void ftrace_raw_event_##call(proto) \ |
206 | { \ | 206 | { \ |
207 | struct ftrace_event_call *call = &event_##call; \ | ||
207 | struct ring_buffer_event *event; \ | 208 | struct ring_buffer_event *event; \ |
208 | struct ftrace_raw_##call *entry; \ | 209 | struct ftrace_raw_##call *entry; \ |
209 | unsigned long irq_flags; \ | 210 | unsigned long irq_flags; \ |
@@ -222,6 +223,9 @@ static void ftrace_raw_event_##call(proto) \ | |||
222 | assign; \ | 223 | assign; \ |
223 | \ | 224 | \ |
224 | trace_current_buffer_unlock_commit(event, irq_flags, pc); \ | 225 | trace_current_buffer_unlock_commit(event, irq_flags, pc); \ |
226 | \ | ||
227 | if (call->preds && !filter_match_preds(call, entry)) \ | ||
228 | ring_buffer_event_discard(event); \ | ||
225 | } \ | 229 | } \ |
226 | \ | 230 | \ |
227 | static int ftrace_raw_reg_event_##call(void) \ | 231 | static int ftrace_raw_reg_event_##call(void) \ |