aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tzanussi@gmail.com>2009-03-22 04:31:04 -0400
committerIngo Molnar <mingo@elte.hu>2009-03-22 13:38:46 -0400
commit7ce7e4249921d5073e764f7ff7ad83cfa9894bd7 (patch)
treed8a3026e85d3230ce39ca99f446abe76a710c337
parent2d622719f1572ef31e0616444a515eba3094d050 (diff)
tracing: add per-event filtering
This patch adds per-event filtering to the event tracing subsystem. It adds a 'filter' debugfs file to each event directory. This file can be written to to set filters; reading from it will display the current set of filters set for that event. Basically, any field listed in the 'format' file for an event can be filtered on (including strings, but not yet other array types) using either matching ('==') or non-matching ('!=') 'predicates'. A 'predicate' can be either a single expression: # echo pid != 0 > filter # cat filter pid != 0 or a compound expression of up to 8 sub-expressions combined using '&&' or '||': # echo comm == Xorg > filter # echo "&& sig != 29" > filter # cat filter comm == Xorg && sig != 29 Only events having field values matching an expression will be available in the trace output; non-matching events are discarded. Note that a compound expression is built up by echoing each sub-expression separately - it's not the most efficient way to do things, but it keeps the parser simple and assumes that compound expressions will be relatively uncommon. In any case, a subsequent patch introducing a way to set filters for entire subsystems should mitigate any need to do this for lots of events. Setting a filter without an '&&' or '||' clears the previous filter completely and sets the filter to the new expression: # cat filter comm == Xorg && sig != 29 # echo comm != Xorg # cat filter comm != Xorg To clear a filter, echo 0 to the filter file: # echo 0 > filter # cat filter none The limit of 8 predicates for a compound expression is arbitrary - for efficiency, it's implemented as an array of pointers to predicates, and 8 seemed more than enough for any filter... Signed-off-by: Tom Zanussi <tzanussi@gmail.com> Acked-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1237710665.7703.48.camel@charm-linux> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-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) \