aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tzanussi@gmail.com>2009-03-22 04:31:17 -0400
committerIngo Molnar <mingo@elte.hu>2009-03-22 13:38:47 -0400
commitcfb180f3e71b2a280a254c8646a9ab1beab63f84 (patch)
tree93fb1a319b40851ebb7a3cc0d9ea4cc9a88e919a
parent7ce7e4249921d5073e764f7ff7ad83cfa9894bd7 (diff)
tracing: add per-subsystem filtering
This patch adds per-subsystem filtering to the event tracing subsystem. It adds a 'filter' debugfs file to each subsystem directory. This file can be written to to set filters; reading from it will display the current set of filters set for that subsystem. Basically what it does is propagate the filter down to each event contained in the subsystem. If a particular event doesn't have a field with the name specified in the filter, it simply doesn't get set for that event. You can verify whether or not the filter was set for a particular event by looking at the filter file for that event. As with per-event filters, compound expressions are supported, echoing '0' to the subsystem's filter file clears all filters in the subsystem, etc. Signed-off-by: Tom Zanussi <tzanussi@gmail.com> Acked-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1237710677.7703.49.camel@charm-linux> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--kernel/trace/trace.h15
-rw-r--r--kernel/trace/trace_events.c86
-rw-r--r--kernel/trace/trace_events_filter.c80
3 files changed, 175 insertions, 6 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index d9eb39e4bb38..f267723c3c52 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -804,6 +804,18 @@ struct ftrace_event_call {
804#endif 804#endif
805}; 805};
806 806
807struct event_subsystem {
808 struct list_head list;
809 const char *name;
810 struct dentry *entry;
811 struct filter_pred **preds;
812};
813
814#define events_for_each(event) \
815 for (event = __start_ftrace_events; \
816 (unsigned long)event < (unsigned long)__stop_ftrace_events; \
817 event++)
818
807#define MAX_FILTER_PRED 8 819#define MAX_FILTER_PRED 8
808 820
809struct filter_pred; 821struct filter_pred;
@@ -832,6 +844,9 @@ extern int filter_add_pred(struct ftrace_event_call *call,
832 struct filter_pred *pred); 844 struct filter_pred *pred);
833extern void filter_free_preds(struct ftrace_event_call *call); 845extern void filter_free_preds(struct ftrace_event_call *call);
834extern int filter_match_preds(struct ftrace_event_call *call, void *rec); 846extern int filter_match_preds(struct ftrace_event_call *call, void *rec);
847extern void filter_free_subsystem_preds(struct event_subsystem *system);
848extern int filter_add_subsystem_pred(struct event_subsystem *system,
849 struct filter_pred *pred);
835 850
836void event_trace_printk(unsigned long ip, const char *fmt, ...); 851void event_trace_printk(unsigned long ip, const char *fmt, ...);
837extern struct ftrace_event_call __start_ftrace_events[]; 852extern struct ftrace_event_call __start_ftrace_events[];
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 97470c48956e..97d4daaddd9a 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -524,6 +524,71 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
524 return cnt; 524 return cnt;
525} 525}
526 526
527static ssize_t
528subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
529 loff_t *ppos)
530{
531 struct event_subsystem *system = filp->private_data;
532 struct trace_seq *s;
533 int r;
534
535 if (*ppos)
536 return 0;
537
538 s = kmalloc(sizeof(*s), GFP_KERNEL);
539 if (!s)
540 return -ENOMEM;
541
542 trace_seq_init(s);
543
544 r = filter_print_preds(system->preds, s->buffer);
545 r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, r);
546
547 kfree(s);
548
549 return r;
550}
551
552static ssize_t
553subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
554 loff_t *ppos)
555{
556 struct event_subsystem *system = filp->private_data;
557 char buf[64], *pbuf = buf;
558 struct filter_pred *pred;
559 int err;
560
561 if (cnt >= sizeof(buf))
562 return -EINVAL;
563
564 if (copy_from_user(&buf, ubuf, cnt))
565 return -EFAULT;
566
567 pred = kzalloc(sizeof(*pred), GFP_KERNEL);
568 if (!pred)
569 return -ENOMEM;
570
571 err = filter_parse(&pbuf, pred);
572 if (err < 0) {
573 filter_free_pred(pred);
574 return err;
575 }
576
577 if (pred->clear) {
578 filter_free_subsystem_preds(system);
579 return cnt;
580 }
581
582 if (filter_add_subsystem_pred(system, pred)) {
583 filter_free_pred(pred);
584 return -EINVAL;
585 }
586
587 *ppos += cnt;
588
589 return cnt;
590}
591
527static const struct seq_operations show_event_seq_ops = { 592static const struct seq_operations show_event_seq_ops = {
528 .start = t_start, 593 .start = t_start,
529 .next = t_next, 594 .next = t_next,
@@ -575,6 +640,12 @@ static const struct file_operations ftrace_event_filter_fops = {
575 .write = event_filter_write, 640 .write = event_filter_write,
576}; 641};
577 642
643static const struct file_operations ftrace_subsystem_filter_fops = {
644 .open = tracing_open_generic,
645 .read = subsystem_filter_read,
646 .write = subsystem_filter_write,
647};
648
578static struct dentry *event_trace_events_dir(void) 649static struct dentry *event_trace_events_dir(void)
579{ 650{
580 static struct dentry *d_tracer; 651 static struct dentry *d_tracer;
@@ -595,18 +666,13 @@ static struct dentry *event_trace_events_dir(void)
595 return d_events; 666 return d_events;
596} 667}
597 668
598struct event_subsystem {
599 struct list_head list;
600 const char *name;
601 struct dentry *entry;
602};
603
604static LIST_HEAD(event_subsystems); 669static LIST_HEAD(event_subsystems);
605 670
606static struct dentry * 671static struct dentry *
607event_subsystem_dir(const char *name, struct dentry *d_events) 672event_subsystem_dir(const char *name, struct dentry *d_events)
608{ 673{
609 struct event_subsystem *system; 674 struct event_subsystem *system;
675 struct dentry *entry;
610 676
611 /* First see if we did not already create this dir */ 677 /* First see if we did not already create this dir */
612 list_for_each_entry(system, &event_subsystems, list) { 678 list_for_each_entry(system, &event_subsystems, list) {
@@ -633,6 +699,14 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
633 system->name = name; 699 system->name = name;
634 list_add(&system->list, &event_subsystems); 700 list_add(&system->list, &event_subsystems);
635 701
702 system->preds = NULL;
703
704 entry = debugfs_create_file("filter", 0444, system->entry, system,
705 &ftrace_subsystem_filter_fops);
706 if (!entry)
707 pr_warning("Could not create debugfs "
708 "'%s/filter' entry\n", name);
709
636 return system->entry; 710 return system->entry;
637} 711}
638 712
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 8e8c5fa25be9..1ab20cee0e4c 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -181,6 +181,27 @@ void filter_free_preds(struct ftrace_event_call *call)
181 } 181 }
182} 182}
183 183
184void filter_free_subsystem_preds(struct event_subsystem *system)
185{
186 struct ftrace_event_call *call = __start_ftrace_events;
187 int i;
188
189 if (system->preds) {
190 for (i = 0; i < MAX_FILTER_PRED; i++)
191 filter_free_pred(system->preds[i]);
192 kfree(system->preds);
193 system->preds = NULL;
194 }
195
196 events_for_each(call) {
197 if (!call->name || !call->regfunc)
198 continue;
199
200 if (!strcmp(call->system, system->name))
201 filter_free_preds(call);
202 }
203}
204
184static int __filter_add_pred(struct ftrace_event_call *call, 205static int __filter_add_pred(struct ftrace_event_call *call,
185 struct filter_pred *pred) 206 struct filter_pred *pred)
186{ 207{
@@ -250,6 +271,65 @@ int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
250 return __filter_add_pred(call, pred); 271 return __filter_add_pred(call, pred);
251} 272}
252 273
274static struct filter_pred *copy_pred(struct filter_pred *pred)
275{
276 struct filter_pred *new_pred = kmalloc(sizeof(*pred), GFP_KERNEL);
277 if (!new_pred)
278 return NULL;
279
280 memcpy(new_pred, pred, sizeof(*pred));
281 if (pred->str_val) {
282 new_pred->str_val = kstrdup(pred->str_val, GFP_KERNEL);
283 new_pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
284 if (!new_pred->str_val) {
285 kfree(new_pred);
286 return NULL;
287 }
288 }
289
290 return new_pred;
291}
292
293int filter_add_subsystem_pred(struct event_subsystem *system,
294 struct filter_pred *pred)
295{
296 struct ftrace_event_call *call = __start_ftrace_events;
297 struct filter_pred *event_pred;
298 int i;
299
300 if (system->preds && !pred->compound)
301 filter_free_subsystem_preds(system);
302
303 if (!system->preds) {
304 system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
305 GFP_KERNEL);
306 if (!system->preds)
307 return -ENOMEM;
308 }
309
310 for (i = 0; i < MAX_FILTER_PRED; i++) {
311 if (!system->preds[i]) {
312 system->preds[i] = pred;
313 break;
314 }
315 if (i == MAX_FILTER_PRED - 1)
316 return -EINVAL;
317 }
318
319 events_for_each(call) {
320 if (!call->name || !call->regfunc)
321 continue;
322
323 if (!strcmp(call->system, system->name)) {
324 event_pred = copy_pred(pred);
325 if (event_pred)
326 filter_add_pred(call, event_pred);
327 }
328 }
329
330 return 0;
331}
332
253int filter_parse(char **pbuf, struct filter_pred *pred) 333int filter_parse(char **pbuf, struct filter_pred *pred)
254{ 334{
255 char *tmp, *tok, *val_str = NULL; 335 char *tmp, *tok, *val_str = NULL;