aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/trace.h28
-rw-r--r--kernel/trace/trace_events.c77
-rw-r--r--kernel/trace/trace_events_filter.c326
-rw-r--r--kernel/trace/trace_events_stage_3.h4
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
45obj-$(CONFIG_EVENT_TRACER) += trace_export.o 45obj-$(CONFIG_EVENT_TRACER) += trace_export.o
46obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o 46obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
47obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o 47obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
48obj-$(CONFIG_EVENT_TRACER) += trace_events_filter.o
48 49
49libftrace-y := ftrace.o 50libftrace-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
809struct filter_pred;
810
811typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event);
812
813struct 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
806int trace_define_field(struct ftrace_event_call *call, char *type, 826int trace_define_field(struct ftrace_event_call *call, char *type,
807 char *name, int offset, int size); 827 char *name, int offset, int size);
828extern void filter_free_pred(struct filter_pred *pred);
829extern int filter_print_preds(struct filter_pred **preds, char *buf);
830extern int filter_parse(char **pbuf, struct filter_pred *pred);
831extern int filter_add_pred(struct ftrace_event_call *call,
832 struct filter_pred *pred);
833extern void filter_free_preds(struct ftrace_event_call *call);
834extern int filter_match_preds(struct ftrace_event_call *call, void *rec);
835
808void event_trace_printk(unsigned long ip, const char *fmt, ...); 836void event_trace_printk(unsigned long ip, const char *fmt, ...);
809extern struct ftrace_event_call __start_ftrace_events[]; 837extern struct ftrace_event_call __start_ftrace_events[];
810extern struct ftrace_event_call __stop_ftrace_events[]; 838extern 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
462static ssize_t
463event_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
487static ssize_t
488event_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
462static const struct seq_operations show_event_seq_ops = { 527static 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
572static 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
507static struct dentry *event_trace_events_dir(void) 578static 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
28static 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
39static 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
50static 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
61static 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
72static 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) */
85int 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
111int 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
147static struct ftrace_event_field *
148find_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
162void 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
172void 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
184static 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
209static int is_string_field(const char *type)
210{
211 if (strchr(type, '[') && strstr(type, "char"))
212 return 1;
213
214 return 0;
215}
216
217int 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
253int 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 \
205static void ftrace_raw_event_##call(proto) \ 205static 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 \
227static int ftrace_raw_reg_event_##call(void) \ 231static int ftrace_raw_reg_event_##call(void) \