diff options
-rw-r--r-- | kernel/trace/trace.h | 15 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 86 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 80 |
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 | ||
807 | struct 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 | ||
809 | struct filter_pred; | 821 | struct 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); |
833 | extern void filter_free_preds(struct ftrace_event_call *call); | 845 | extern void filter_free_preds(struct ftrace_event_call *call); |
834 | extern int filter_match_preds(struct ftrace_event_call *call, void *rec); | 846 | extern int filter_match_preds(struct ftrace_event_call *call, void *rec); |
847 | extern void filter_free_subsystem_preds(struct event_subsystem *system); | ||
848 | extern int filter_add_subsystem_pred(struct event_subsystem *system, | ||
849 | struct filter_pred *pred); | ||
835 | 850 | ||
836 | void event_trace_printk(unsigned long ip, const char *fmt, ...); | 851 | void event_trace_printk(unsigned long ip, const char *fmt, ...); |
837 | extern struct ftrace_event_call __start_ftrace_events[]; | 852 | extern 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 | ||
527 | static ssize_t | ||
528 | subsystem_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 | |||
552 | static ssize_t | ||
553 | subsystem_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 | |||
527 | static const struct seq_operations show_event_seq_ops = { | 592 | static 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 | ||
643 | static 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 | |||
578 | static struct dentry *event_trace_events_dir(void) | 649 | static 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 | ||
598 | struct event_subsystem { | ||
599 | struct list_head list; | ||
600 | const char *name; | ||
601 | struct dentry *entry; | ||
602 | }; | ||
603 | |||
604 | static LIST_HEAD(event_subsystems); | 669 | static LIST_HEAD(event_subsystems); |
605 | 670 | ||
606 | static struct dentry * | 671 | static struct dentry * |
607 | event_subsystem_dir(const char *name, struct dentry *d_events) | 672 | event_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 | ||
184 | void 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 | |||
184 | static int __filter_add_pred(struct ftrace_event_call *call, | 205 | static 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 | ||
274 | static 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 | |||
293 | int 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 | |||
253 | int filter_parse(char **pbuf, struct filter_pred *pred) | 333 | int filter_parse(char **pbuf, struct filter_pred *pred) |
254 | { | 334 | { |
255 | char *tmp, *tok, *val_str = NULL; | 335 | char *tmp, *tok, *val_str = NULL; |